Dependency Graph
Shape shows declaration kind, border shows statement status, fill shows proof status, and edge style separates statement from proof dependencies.
Muted nodes are project declarations without an authored showcase node, shown with their real Lean dependencies.
Node outline shows whether the item is definition-like or theorem-like.
Border color tracks whether the statement is blocked, ready, or already formalized.
Fill color tracks proof readiness independently from statement progress.
Border treatments flag missing references, missing declarations, or Lean-only nodes without an informal statement.
Line style distinguishes statement dependencies from proof-only dependencies; intent and origin add further styling.
Group View uses tab-shaped aggregate group nodes; labels use group titles, colors are averaged over child nodes, and individual warning markers are hidden.
Group nodes summarize children instead of showing each declaration separately.
Grouped edges compress many child edges into one aggregate connection.
Modulus on the real segment. Under the bounded-race hypothesis, for real \sigma > 1/2
\bigl\| L(\sigma, \chi) \bigr\| \;=\; \exp\bigl(\mathrm{Re}\, G(\sigma)\bigr).
Lean code for Lemma norm_LFunction_eq_exp_re_contLog
-
⟲
theorem norm_LFunction_eq_exp_re_contLog {C
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).kℕ‖ ≤ Cℝ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ1 / 2 < σ: 1ℝ/ 2ℝ< σℝ) : ‖DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions. Note that this is not the same as `LSeries χ`: they agree in the convergence range, but `LSeries χ s` is defined to be `0` if `re s ≤ 1`.χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)‖ = Real.expReal.exp (x : ℝ) : ℝThe real exponential function, defined as the real part of the complex exponential((contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi` is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3` layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}` (`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`).((σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)).reComplex.re (self : ℂ) : ℝThe real part of a complex number.)theorem norm_LFunction_eq_exp_re_contLog {C
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).kℕ‖ ≤ Cℝ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ1 / 2 < σ: 1ℝ/ 2ℝ< σℝ) : ‖DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions. Note that this is not the same as `LSeries χ`: they agree in the convergence range, but `LSeries χ s` is defined to be `0` if `re s ≤ 1`.χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)‖ = Real.expReal.exp (x : ℝ) : ℝThe real exponential function, defined as the real part of the complex exponential((contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi` is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3` layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}` (`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`).((σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)).reComplex.re (self : ℂ) : ℝThe real part of a complex number.)
The identity theorem gives L(\sigma, \chi) = \exp(G(\sigma)) on \Omega, and
\sigma > 1/2 lies in \Omega. Taking norms,
\|\exp(G(\sigma))\| = \exp(\mathrm{Re}\, G(\sigma)). \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hmem↑σ ∈ {s | 1 / 2 < s.re} : ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ∈ {sℂ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. | 1ℝ / 2ℝ < sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number. } := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[Set.mem_setOf_eqSet.mem_setOf_eq.{u} {α : Type u} {x : α} {p : α → Prop} : (x ∈ {y | p y}) = p x, Complex.ofReal_reComplex.ofReal_re (r : ℝ) : (↑r).re = r]
exact`exact e` closes the main goal if its target type matches that of `e`.
hσ1 / 2 < σ
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hexpComplex.exp (contLog ↑σ) = DirichletCharacter.LFunction χ ↑σ : Complex.expComplex.exp (z : ℂ) : ℂThe complex exponential function, defined via its Taylor series (contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi`
is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3`
layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}`
(`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`). ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )) = DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. σℝ :=
exp_contLog_eqOnA362583.exp_contLog_eqOn {C : ℝ} (hB : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C) :
Set.EqOn (fun s => Complex.exp (contLog s)) (DirichletCharacter.LFunction χ) {s | 1 / 2 < s.re}**Identity theorem**: under the bounded-race hypothesis,
`exp ∘ contLog = L(χ, ·)` on all of `Ω = {Re s > 1/2}` — `Ω` is open and preconnected,
both sides are holomorphic on `Ω` and agree on the open `{Re s > 1} ∋ 2`. hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C hmem↑σ ∈ {s | 1 / 2 < s.re}
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[← hexpComplex.exp (contLog ↑σ) = DirichletCharacter.LFunction χ ↑σ, Complex.norm_expComplex.norm_exp (z : ℂ) : ‖Complex.exp z‖ = Real.exp z.re]
Layer split. For \mathrm{Re}\, s > 1,
\sum_p -\operatorname{Log}\bigl(1 - \chi(p)\, p^{-s}\bigr) \;=\; A(s) + B(s) + T(s).
Lean code for Theorem tsum_neg_log_eq_layers
-
⟲
theorem tsum_neg_log_eq_layers {s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs1 < s.re: 1ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : ∑' pNat.Primes: Nat.PrimesNat.Primes : TypeThe type of prime numbers, -Complex.logComplex.log (x : ℂ) : ℂInverse of the `exp` function. Returns values such that `(log x).im > - π` and `(log x).im ≤ π`. `log 0 = 0`(1ℂ- χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`.4ℕ) * ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ)) = layerAA362583.layerA (s : ℂ) : ℂThe `k = 1` layer `A(s) = Σ_p χ(p) p^(-s)`.sℂ+ layerBA362583.layerB (s : ℂ) : ℂThe `k = 2` layer `B(s) = (1/2) Σ_{p odd} p^(-2s)`, with an `if`-mask at `p = 2` (`χ(2)² = 0`, `χ(p)² = 1` for odd `p`; see `χ_sq_eq_ite`, `tsum_term_two_eq_layerB`). Exponent convention: `-(2 * s)`.sℂ+ layerTA362583.layerT (s : ℂ) : ℂThe `k ≥ 3` layer `T(s) = Σ_p t_p(s)`.sℂtheorem tsum_neg_log_eq_layers {s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs1 < s.re: 1ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : ∑' pNat.Primes: Nat.PrimesNat.Primes : TypeThe type of prime numbers, -Complex.logComplex.log (x : ℂ) : ℂInverse of the `exp` function. Returns values such that `(log x).im > - π` and `(log x).im ≤ π`. `log 0 = 0`(1ℂ- χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`.4ℕ) * ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ)) = layerAA362583.layerA (s : ℂ) : ℂThe `k = 1` layer `A(s) = Σ_p χ(p) p^(-s)`.sℂ+ layerBA362583.layerB (s : ℂ) : ℂThe `k = 2` layer `B(s) = (1/2) Σ_{p odd} p^(-2s)`, with an `if`-mask at `p = 2` (`χ(2)² = 0`, `χ(p)² = 1` for odd `p`; see `χ_sq_eq_ite`, `tsum_term_two_eq_layerB`). Exponent convention: `-(2 * s)`.sℂ+ layerTA362583.layerT (s : ℂ) : ℂThe `k ≥ 3` layer `T(s) = Σ_p t_p(s)`.sℂ
Rewrite each summand by the per-prime split, turning the left side into
\sum_p \bigl(\chi(p)\, p^{-s} + \tfrac12\, \chi(p)^2\, p^{-2s} + t_p(s)\bigr). The three
resulting prime-indexed series converge absolutely — dominated by p^{-\sigma},
\tfrac12 p^{-2\sigma}, and the geometric-tail bound \tfrac43 p^{-3/2} respectively — so
linearity of convergent series splits the sum into \sum_p \chi(p)\, p^{-s},
\sum_p \tfrac12 \chi(p)^2\, p^{-2s}, and \sum_p t_p(s). The first is A(s) and the third
is T(s) by definition; the middle piece is B(s), since \chi(2)^2 = 0 and
\chi(p)^2 = 1 for odd p. No absolutely-summable-double-family machinery appears.
\blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h1Summable fun p => χ ↑↑p * ↑↑p ^ (-s) : SummableSummable.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (f : β → α)
(L : SummationFilter β := SummationFilter.unconditional β) : Prop`Summable f` means that `f` has some (infinite) sum with respect to `L`. Use `tsum` to get the
value. (fun pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers ↦ χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)) :=
summable_layerA_termA362583.summable_layerA_term {s : ℂ} (hs : 1 < s.re) : Summable fun p => χ ↑↑p * ↑↑p ^ (-s)Summability of the `layerA` integrand for `Re s > 1`. hs1 < s.re
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h2Summable fun p => χ ↑↑p ^ 2 * ↑↑p ^ (-(2 * s)) / 2 : SummableSummable.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (f : β → α)
(L : SummationFilter β := SummationFilter.unconditional β) : Prop`Summable f` means that `f` has some (infinite) sum with respect to `L`. Use `tsum` to get the
value.
(fun pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers ↦ χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) ^ 2ℕ * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-(2ℂ * sℂ)) / 2ℂ) :=
(summable_norm_term_twoA362583.summable_norm_term_two {s : ℂ} (hs : 1 / 2 < s.re) : Summable fun p => ‖χ ↑↑p ^ 2 * ↑↑p ^ (-(2 * s)) / 2‖Absolute summability of the `k = 2` terms for `Re s > 1/2`
(domination by `p^(-2 Re s)`). (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
)).of_normSummable.of_norm.{u_1, u_3} {ι : Type u_1} {E : Type u_3} [SeminormedAddCommGroup E] [CompleteSpace E] {f : ι → E}
(hf : Summable fun a => ‖f a‖) : Summable f
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h3Summable fun p => tp p s : SummableSummable.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (f : β → α)
(L : SummationFilter β := SummationFilter.unconditional β) : Prop`Summable f` means that `f` has some (infinite) sum with respect to `L`. Use `tsum` to get the
value. (fun pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers ↦ tpA362583.tp (p : Nat.Primes) (s : ℂ) : ℂPer-prime `k ≥ 3` tail `t_p(s) = Σ_{k≥0} (χ(p) p^(-s))^(k+3) / (k+3)`.
The compact form `z_p^(k+3)/(k+3)` (rather than the expanded
`χ(p)^(k+3) p^(-(k+3)s)/(k+3)`) is chosen so that the tail produced by peeling three
terms off `hasSum_neg_log_one_sub` is *definitionally* `tp p s` (see `neg_log_split`). pNat.Primes sℂ) := summable_tpA362583.summable_tp {s : ℂ} (hs : 1 / 2 ≤ s.re) : Summable fun p => tp p sPlain summability of the `layerT` integrand on `Re s ≥ 1/2`. (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
)
calcStep-wise reasoning over transitive relations.
```
calc
a = b := pab
b = c := pbc
...
y = z := pyz
```
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
relation implementing the typeclass `Trans`. Instead of repeating the right-
hand sides, subsequent left-hand sides can be replaced with `_`.
```
calc
a = b := pab
_ = c := pbc
...
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc
_ = bce := pabce
_ = cef := pbcef
...
_ = xyz := pwxyz
```
`calc` works as a term, as a tactic or as a `conv` tactic.
See [Theorem Proving in Lean 4][tpil4] for more information.
[tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , -Complex.logComplex.log (x : ℂ) : ℂInverse of the `exp` function. Returns values such that `(log x).im > - π` and `(log x).im ≤ π`.
`log 0 = 0` (1ℂ - χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ))
= ∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , (χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)
+ χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) ^ 2ℕ * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-(2ℂ * sℂ)) / 2ℂ + tpA362583.tp (p : Nat.Primes) (s : ℂ) : ℂPer-prime `k ≥ 3` tail `t_p(s) = Σ_{k≥0} (χ(p) p^(-s))^(k+3) / (k+3)`.
The compact form `z_p^(k+3)/(k+3)` (rather than the expanded
`χ(p)^(k+3) p^(-(k+3)s)/(k+3)`) is chosen so that the tail produced by peeling three
terms off `hasSum_neg_log_one_sub` is *definitionally* `tp p s` (see `neg_log_split`). pNat.Primes sℂ) :=
tsum_congrtsum_congr.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] {L : SummationFilter β}
{f g : β → α} (hfg : ∀ (b : β), f b = g b) : ∑'[L] (b : β), f b = ∑'[L] (b : β), g b fun pNat.Primes ↦ neg_log_splitA362583.neg_log_split (p : Nat.Primes) {s : ℂ} (hs : 1 < s.re) :
-Complex.log (1 - χ ↑↑p * ↑↑p ^ (-s)) = χ ↑↑p * ↑↑p ^ (-s) + χ ↑↑p ^ 2 * ↑↑p ^ (-(2 * s)) / 2 + tp p s**Per-prime split**: for `Re s > 1`,
`-log(1 - χ(p) p^(-s)) = χ(p) p^(-s) + χ(p)² p^(-2s)/2 + t_p(s)`.
Proof: peel three terms off the log series with `Summable.sum_add_tsum_nat_add 3`
+ `Finset.sum_range_succ`; the `k = 0` term is `0`, the tail is definitionally
`tp p s`. No rearrangement is involved. pNat.Primes hs1 < s.re
_ = (∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , (χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)
+ χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) ^ 2ℕ * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-(2ℂ * sℂ)) / 2ℂ))
+ ∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , tpA362583.tp (p : Nat.Primes) (s : ℂ) : ℂPer-prime `k ≥ 3` tail `t_p(s) = Σ_{k≥0} (χ(p) p^(-s))^(k+3) / (k+3)`.
The compact form `z_p^(k+3)/(k+3)` (rather than the expanded
`χ(p)^(k+3) p^(-(k+3)s)/(k+3)`) is chosen so that the tail produced by peeling three
terms off `hasSum_neg_log_one_sub` is *definitionally* `tp p s` (see `neg_log_split`). pNat.Primes sℂ := (h1Summable fun p => χ ↑↑p * ↑↑p ^ (-s).addSummable.add.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] {f g : β → α}
{L : SummationFilter β} [ContinuousAdd α] (hf : Summable f L) (hg : Summable g L) : Summable (fun b => f b + g b) L h2Summable fun p => χ ↑↑p ^ 2 * ↑↑p ^ (-(2 * s)) / 2).tsum_addSummable.tsum_add.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] {f g : β → α}
{L : SummationFilter β} [T2Space α] [ContinuousAdd α] [L.NeBot] (hf : Summable f L) (hg : Summable g L) :
∑'[L] (b : β), (f b + g b) = ∑'[L] (b : β), f b + ∑'[L] (b : β), g b h3Summable fun p => tp p s
_ = ((∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ))
+ ∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) ^ 2ℕ * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-(2ℂ * sℂ)) / 2ℂ)
+ ∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , tpA362583.tp (p : Nat.Primes) (s : ℂ) : ℂPer-prime `k ≥ 3` tail `t_p(s) = Σ_{k≥0} (χ(p) p^(-s))^(k+3) / (k+3)`.
The compact form `z_p^(k+3)/(k+3)` (rather than the expanded
`χ(p)^(k+3) p^(-(k+3)s)/(k+3)`) is chosen so that the tail produced by peeling three
terms off `hasSum_neg_log_one_sub` is *definitionally* `tp p s` (see `neg_log_split`). pNat.Primes sℂ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[h1Summable fun p => χ ↑↑p * ↑↑p ^ (-s).tsum_addSummable.tsum_add.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] {f g : β → α}
{L : SummationFilter β} [T2Space α] [ContinuousAdd α] [L.NeBot] (hf : Summable f L) (hg : Summable g L) :
∑'[L] (b : β), (f b + g b) = ∑'[L] (b : β), f b + ∑'[L] (b : β), g b h2Summable fun p => χ ↑↑p ^ 2 * ↑↑p ^ (-(2 * s)) / 2]
_ = layerAA362583.layerA (s : ℂ) : ℂThe `k = 1` layer `A(s) = Σ_p χ(p) p^(-s)`. sℂ + layerBA362583.layerB (s : ℂ) : ℂThe `k = 2` layer `B(s) = (1/2) Σ_{p odd} p^(-2s)`, with an `if`-mask at
`p = 2` (`χ(2)² = 0`, `χ(p)² = 1` for odd `p`; see `χ_sq_eq_ite`,
`tsum_term_two_eq_layerB`). Exponent convention: `-(2 * s)`. sℂ + layerTA362583.layerT (s : ℂ) : ℂThe `k ≥ 3` layer `T(s) = Σ_p t_p(s)`. sℂ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[tsum_term_two_eq_layerBA362583.tsum_term_two_eq_layerB (s : ℂ) : ∑' (p : Nat.Primes), χ ↑↑p ^ 2 * ↑↑p ^ (-(2 * s)) / 2 = layerB sSumming the `k = 2` terms over primes gives exactly `layerB` (via
`χ_sq_eq_ite`; unconditional). ]; rflThis tactic applies to a goal whose target has the form `x ~ x`,
where `~` is equality, heterogeneous equality or any relation that
has a reflexivity lemma tagged with the attribute @[refl].
Layer A as a Dirichlet series. A(s) = \sum_{n \ge 0} f_\chi(n)\, n^{-s}. This exhibits
A as the \mathbb{N}-indexed Dirichlet series of f_\chi, so its by-parts series
continues A.
Lean code for Lemma layerA_eq_tsum_fChi
-
⟲
theorem layerA_eq_tsum_fChi (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : layerAA362583.layerA (s : ℂ) : ℂThe `k = 1` layer `A(s) = Σ_p χ(p) p^(-s)`.sℂ= ∑' nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).nℕ* (nℕ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ)theorem layerA_eq_tsum_fChi (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : layerAA362583.layerA (s : ℂ) : ℂThe `k = 1` layer `A(s) = Σ_p χ(p) p^(-s)`.sℂ= ∑' nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).nℕ* (nℕ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ)
The layer A(s) = \sum_p \chi(p)\, p^{-s} is a sum over the subtype of primes; rewriting it
as a sum over all n \in \mathbb{N} inserts the prime indicator, giving
\sum_n \mathbf{1}_{\text{prime}}(n)\, \chi(n)\, n^{-s}. At a prime n the indicator leaves
\chi(n)\, n^{-s} = f_\chi(n)\, n^{-s} (the bridge \chi = \kappa on natural casts); off the
primes both f_\chi(n) and the indicator vanish. Hence A(s) = \sum_n f_\chi(n)\, n^{-s}.
Unconditional — a subtype-versus-indicator rewriting; no summability is needed. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h∑' (x : ↑{n | Nat.Prime n}), χ ↑↑x * ↑↑x ^ (-s) = ∑' (x : ℕ), {n | Nat.Prime n}.indicator (fun n => χ ↑n * ↑n ^ (-s)) x := tsum_subtypetsum_subtype.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (s : Set β) (f : β → α) :
∑' (x : ↑s), f ↑x = ∑' (x : β), s.indicator f x {nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
| nℕ.PrimeNat.Prime (p : ℕ) : Prop`Nat.Prime p` means that `p` is a prime number, that is, a natural number
at least 2 whose only divisors are `p` and `1`.
The theorem `Nat.prime_def` witnesses this description of a prime number. }
(fun nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
↦ χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. (nℕ : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * (nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ))
calcStep-wise reasoning over transitive relations.
```
calc
a = b := pab
b = c := pbc
...
y = z := pyz
```
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
relation implementing the typeclass `Trans`. Instead of repeating the right-
hand sides, subsequent left-hand sides can be replaced with `_`.
```
calc
a = b := pab
_ = c := pbc
...
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc
_ = bce := pabce
_ = cef := pbcef
...
_ = xyz := pwxyz
```
`calc` works as a term, as a tactic or as a `conv` tactic.
See [Theorem Proving in Lean 4][tpil4] for more information.
[tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
layerAA362583.layerA (s : ℂ) : ℂThe `k = 1` layer `A(s) = Σ_p χ(p) p^(-s)`. sℂ
= ∑' nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
, Set.indicatorSet.indicator.{u_1, u_3} {α : Type u_1} {M : Type u_3} [Zero M] (s : Set α) (f : α → M) (x : α) : M`Set.indicator s f a` is `f a` if `a ∈ s`, `0` otherwise. {nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
| nℕ.PrimeNat.Prime (p : ℕ) : Prop`Nat.Prime p` means that `p` is a prime number, that is, a natural number
at least 2 whose only divisors are `p` and `1`.
The theorem `Nat.prime_def` witnesses this description of a prime number. }
(fun nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
↦ χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. (nℕ : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * (nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)) nℕ := h∑' (x : ↑{n | Nat.Prime n}), χ ↑↑x * ↑↑x ^ (-s) = ∑' (x : ℕ), {n | Nat.Prime n}.indicator (fun n => χ ↑n * ↑n ^ (-s)) x
_ = ∑' nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
, fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at
primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`),
so `Complex.bpSeries fChi` is the by-parts continuation of `layerA`
(via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`). nℕ * (nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
tsum_congrtsum_congr.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] {L : SummationFilter β}
{f g : β → α} (hfg : ∀ (b : β), f b = g b) : ∑'[L] (b : β), f b = ∑'[L] (b : β), g b fun nℕ ↦ ?_
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[Set.indicator_applySet.indicator_apply.{u_1, u_3} {α : Type u_1} {M : Type u_3} [Zero M] (s : Set α) (f : α → M) (a : α)
[Decidable (a ∈ s)] : s.indicator f a = if a ∈ s then f a else 0]
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[Set.mem_setOf_eqSet.mem_setOf_eq.{u} {α : Type u} {x : α} {p : α → Prop} : (x ∈ {y | p y}) = p x]
unfold* `unfold id` unfolds all occurrences of definition `id` in the target.
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
* `unfold id at h` unfolds at the hypothesis `h`.
Definitions can be either global or local definitions.
For non-recursive global definitions, this tactic is identical to `delta`.
For recursive global definitions, it uses the "unfolding lemma" `id.eq_def`,
which is generated for each recursive definition, to unfold according to the recursive definition given by the user.
Only one level of unfolding is performed, in contrast to `simp only [id]`, which unfolds definition `id` recursively.
fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at
primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`),
so `Complex.bpSeries fChi` is the by-parts continuation of `layerA`
(via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).
by_cases`by_cases (h :)? p` splits the main goal into two cases, assuming `h : p` in the first branch, and `h : ¬ p` in the second branch.
hnNat.Prime n : nℕ.PrimeNat.Prime (p : ℕ) : Prop`Nat.Prime p` means that `p` is a prime number, that is, a natural number
at least 2 whose only divisors are `p` and `1`.
The theorem `Nat.prime_def` witnesses this description of a prime number.
· rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[if_posif_pos.{u} {c : Prop} {h : Decidable c} (hc : c) {α : Sort u} {t e : α} : (if c then t else e) = t hnNat.Prime n, if_posif_pos.{u} {c : Prop} {h : Decidable c} (hc : c) {α : Sort u} {t e : α} : (if c then t else e) = t hnNat.Prime n, χ_natCast_eq_kernelA362583.χ_natCast_eq_kernel (n : ℕ) : χ ↑n = ↑(raceKernel n)Elementary bridge: on natural casts, `χ` is `raceKernel` (the integrand of
`raceSum`), connecting the analytic layer to `raceSum`. ]
· rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[if_negif_neg.{u} {c : Prop} {h : Decidable c} (hnc : ¬c) {α : Sort u} {t e : α} : (if c then t else e) = e hn¬Nat.Prime n, if_negif_neg.{u} {c : Prop} {h : Decidable c} (hnc : ¬c) {α : Sort u} {t e : α} : (if c then t else e) = e hn¬Nat.Prime n, zero_mulMulZeroClass.zero_mul.{u} {M₀ : Type u} [self : MulZeroClass M₀] (a : M₀) : 0 * a = 0Zero is a left absorbing element for multiplication ]
For k \ge 0, let p_k denote the k-th odd prime, so p_0 = 3, p_1 = 5,
p_2 = 7, and so on. In Lean this is the (k+1)-st prime in Mathlib's enumeration
(Nat.nth): since the zeroth prime is 2, shifting the index by one skips exactly the prime
2.
Lean code for Definition oddPrime
-
⟲
def oddPrime (k
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.def oddPrime (k
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
⟲:= Nat.nthNat.nth (p : ℕ → Prop) (n : ℕ) : ℕFind the `n`-th natural number satisfying `p` (indexed from `0`, so `nth p 0` is the first
natural number satisfying `p`), or `0` if there is no such number. See also
`Subtype.orderIsoOfNat` for the order isomorphism with ℕ when `p` is infinitely often true. Nat.PrimeNat.Prime (p : ℕ) : Prop`Nat.Prime p` means that `p` is a prime number, that is, a natural number
at least 2 whose only divisors are `p` and `1`.
The theorem `Nat.prime_def` witnesses this description of a prime number. (kℕ + 1ℕ)
The set \{k \mid b_k = 0\} is infinite. Together with the previous theorem: the digit
sequence is neither eventually all ones nor eventually all zeros, which is exactly what the
eventual-periodicity argument needs to keep the binary tails strictly between 0 and 1.
Lean code for Theorem bits_infinite_zeros
-
≈
theorem bits_infinite_zeros : {k
ℕ| bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).kℕ= 0ℕ}.InfiniteSet.Infinite.{u} {α : Type u} (s : Set α) : PropA set is infinite if it is not finite. This is protected so that it does not conflict with global `Infinite`.theorem bits_infinite_zeros : {k
ℕ| bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).kℕ= 0ℕ}.InfiniteSet.Infinite.{u} {α : Type u} (s : Set α) : PropA set is infinite if it is not finite. This is protected so that it does not conflict with global `Infinite`.
The same argument as the previous theorem, applied to the residue class 1 \bmod 4:
Dirichlet's theorem supplies infinitely many primes \equiv 1 \pmod 4, the odd-prime
enumeration injects them into the index set, and for such an index k, p_k \equiv 1 \pmod 4
is in particular \not\equiv 3 \pmod 4, so b_k = 0. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hidx := infinite_oddPrime_index infinite_primes_one_mod_four
(fun p hp ↦ hp.1) (fun h ↦ absurd h.2 (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. decide`decide` attempts to prove the main goal (with target type `p`) by synthesizing an instance of `Decidable p`
and then reducing that instance to evaluate the truth value of `p`.
If it reduces to `isTrue h`, then `h` is a proof of `p` that closes the goal.
The target is not allowed to contain local variables or metavariables.
If there are local variables, you can first try using the `revert` tactic with these local variables to move them into the target,
or you can use the `+revert` option, described below.
Options:
- `decide +revert` begins by reverting local variables that the target depends on,
after cleaning up the local context of irrelevant variables.
A variable is *relevant* if it appears in the target, if it appears in a relevant variable,
or if it is a proposition that refers to a relevant variable.
- `decide +kernel` uses kernel for reduction instead of the elaborator.
It has two key properties: (1) since it uses the kernel, it ignores transparency and can unfold everything,
and (2) it reduces the `Decidable` instance only once instead of twice.
- `decide +native` uses the native code compiler (`#eval`) to evaluate the `Decidable` instance,
admitting the result via an axiom. This can be significantly more efficient than using reduction, but it is at the cost of increasing the size
This can be significantly more efficient than using reduction, but it is at the cost of increasing the size
of the trusted code base.
Namely, it depends on the correctness of the Lean compiler and all definitions with an `@[implemented_by]` attribute.
Like with `+kernel`, the `Decidable` instance is evaluated only once.
Limitation: In the default mode or `+kernel` mode, since `decide` uses reduction to evaluate the term,
`Decidable` instances defined by well-founded recursion might not work because evaluating them requires reducing proofs.
Reduction can also get stuck on `Decidable` instances with `Eq.rec` terms.
These can appear in instances defined using tactics (such as `rw` and `simp`).
To avoid this, create such instances using definitions such as `decidable_of_iff` instead.
## Examples
Proving inequalities:
```lean
example : 2 + 2 ≠ 5 := by decide
```
Trying to prove a false proposition:
```lean
example : 1 ≠ 1 := by decide
/-
tactic 'decide' proved that the proposition
1 ≠ 1
is false
-/
```
Trying to prove a proposition whose `Decidable` instance fails to reduce
```lean
opaque unknownProp : Prop
open scoped Classical in
example : unknownProp := by decide
/-
tactic 'decide' failed for proposition
unknownProp
since its 'Decidable' instance reduced to
Classical.choice ⋯
rather than to the 'isTrue' constructor.
-/
```
## Properties and relations
For equality goals for types with decidable equality, usually `rfl` can be used in place of `decide`.
```lean
example : 1 + 1 = 2 := by decide
example : 1 + 1 = 2 := by rfl
```
))
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
hidx.mono fun k hk ↦ ?_
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h1 : oddPrime k % 4 = 1 := hk.2
exact`exact e` closes the main goal if its target type matches that of `e`.
bit_eq_zero_iff.mpr (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. omegaThe `omega` tactic, for resolving integer and natural linear arithmetic problems.
It is not yet a full decision procedure (no "dark" or "grey" shadows),
but should be effective on many problems.
We handle hypotheses of the form `x = y`, `x < y`, `x ≤ y`, and `k ∣ x` for `x y` in `Nat` or `Int`
(and `k` a literal), along with negations of these statements.
We decompose the sides of the inequalities as linear combinations of atoms.
If we encounter `x / k` or `x % k` for literal integers `k` we introduce new auxiliary variables
and the relevant inequalities.
On the first pass, we do not perform case splits on natural subtraction.
If `omega` fails, we recursively perform a case split on
a natural subtraction appearing in a hypothesis, and try again.
The options
```
omega +splitDisjunctions +splitNatSub +splitNatAbs +splitMinMax
```
can be used to:
* `splitDisjunctions`: split any disjunctions found in the context,
if the problem is not otherwise solvable.
* `splitNatSub`: for each appearance of `((a - b : Nat) : Int)`, split on `a ≤ b` if necessary.
* `splitNatAbs`: for each appearance of `Int.natAbs a`, split on `0 ≤ a` if necessary.
* `splitMinMax`: for each occurrence of `min a b`, split on `min a b = a ∨ min a b = b`
Currently, all of these are on by default.
)
Prime-sum divergence. For every M and \eta > 0 there is \sigma \in (1, 1+\eta) with
P(\sigma) := \sum_p p^{-\sigma} > M.
Lean code for Lemma exists_one_lt_tsum_primes_rpow_gt
-
≈
theorem exists_one_lt_tsum_primes_rpow_gt (M
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) {ηℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hη0 < η: 0ℝ< ηℝ) : ∃ σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., 1ℝ< σℝ∧ σℝ< 1ℝ+ ηℝ∧ Mℝ< ∑' pNat.Primes: Nat.PrimesNat.Primes : TypeThe type of prime numbers, ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) ^ (-σℝ)theorem exists_one_lt_tsum_primes_rpow_gt (M
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) {ηℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hη0 < η: 0ℝ< ηℝ) : ∃ σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., 1ℝ< σℝ∧ σℝ< 1ℝ+ ηℝ∧ Mℝ< ∑' pNat.Primes: Nat.PrimesNat.Primes : TypeThe type of prime numbers, ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) ^ (-σℝ)
Because \sum_p 1/p diverges — the sole quantitative prime input of the whole development —
its finite subsums are unbounded, so choose a finite set F of primes with
\sum_{p \in F} 1/p > M. At \sigma = 1 the finite sum \sum_{p \in F} p^{-\sigma} equals
\sum_{p \in F} 1/p > M; being continuous in \sigma, it still exceeds M at some
\sigma \in (1, 1+\eta). There \sigma > 1, so the full series P(\sigma) = \sum_p p^{-\sigma}
converges, and its nonnegative terms make it at least the finite subsum:
P(\sigma) \ge \sum_{p \in F} p^{-\sigma} > M. A single point \sigma^* suffices — no
filters or limits appear. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨F, hF⟩ := exists_finset_gt_of_not_summable (fun p ↦ by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. positivity`positivity` solves goals of the form `0 ≤ x`, `0 < x` and `x ≠ 0`. The tactic works recursively
according to the syntax of the expression `x`, by attempting to prove subexpressions are
positive/nonnegative/nonzero and combining this into a final proof. This tactic either closes the
goal or fails.
For each subexpression `e`, `positivity` will try to:
* try `@[positivity]`-tagged extensions to recursively prove `e` is positive/nonnegative/nonzero
based on its subexpressions (see the `positivity` attribute for more details), or
* try the `norm_num` tactic to prove `e` is positive/nonnegative/nonzero, or
* try showing `e : t` is nonnegative because there is a `CanonicallyOrderedAdd t` instance, or
* use a local hypothesis of the form `0 ≤ e`, `0 < e` or `e ≠ 0`.
This tactic is extensible. See the `positivity` attribute documentation for more details.
* `positivity [t₁, …, tₙ]` first executes `have := t₁; …; have := tₙ` in the current goal,
then runs `positivity`. This is useful when `positivity` needs derived premises such as `0 < y`
for division/reciprocal, or `0 ≤ x` for real powers.
Examples:
```
example {a : ℤ} (ha : 3 < a) : 0 ≤ a ^ 3 + a := by positivity
example {a : ℤ} (ha : 1 < a) : 0 < |(3:ℤ) + a| := by positivity
example {b : ℤ} : 0 ≤ max (-3) (b ^ 2) := by positivity
example {a b c d : ℝ} (hab : 0 < a * b) (hb : 0 ≤ b) (hcd : c < d) :
0 < a ^ c + 1 / (d - c) := by
positivity [sub_pos_of_lt hcd, pos_of_mul_pos_left hab hb]
```
)
Nat.Primes.not_summable_one_div M
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hF1 : M < ∑ p ∈ F, ((p : ℕ) : ℝ) ^ (-(1 * (1 : ℝ))) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
hF.trans_le (le_of_eq (Finset.sum_congr rfl fun p _ ↦ ?_))
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[show -(1 * (1 : ℝ)) = -1 by norm_num`norm_num` normalizes numerical expressions in the goal. By default, it supports the operations
`+` `-` `*` `/` `⁻¹` `^` and `%` over types with (at least) an `AddMonoidWithOne` instance, such as
`ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ`. In addition to evaluating numerical expressions, `norm_num` will use `simp`
to simplify the goal. If the goal has the form `A = B`, `A ≠ B`, `A < B` or `A ≤ B`, where `A` and
`B` are numerical expressions, `norm_num` will try to close it. It also has a relatively simple
primality prover (available if you import `Mathlib.Tactic.NormNum.Prime`).
This tactic is extensible. Extensions can allow `norm_num` to evaluate more kinds of expressions, or
to prove more kinds of propositions (such as, primality of natural numbers). See the `@[norm_num]`
attribute for further information on extending `norm_num`.
* `norm_num at l` normalizes at location(s) `l`.
* `norm_num [h1, ...]` adds the arguments `h1, ...` to the `simp` set in addition to the default
`simp` set. All options for `simp` arguments are supported, in particular `←`, `↑` and `↓`.
* `norm_num only` does not use the default `simp` set for simplification. `norm_num only [h1, ...]`
uses only the arguments `h1, ...` in addition to the routines tagged `@[norm_num]`.
`norm_num only` still performs post-processing steps, like `simp only`, use `norm_num1` if you
exclusively want to normalize numerical expressions.
* `norm_num (config := cfg)` uses `cfg` as configuration for `simp` calls (see the `simp` tactic for
further details).
Examples:
```lean
example : 43 ≤ 74 + (33 : ℤ) := by norm_num
example : ¬ (7-2)/(2*3) ≥ (1:ℝ) + 2/(3^2) := by norm_num
```
, Real.rpow_neg_one, one_div]
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨u, hu1, hu2, hu3⟩ := exists_right_of_sum_rpow_gt hF1 hη
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hsum : Summable (fun p : Nat.Primes ↦ ((p : ℕ) : ℝ) ^ (-u)) :=
Nat.Primes.summable_rpow.mpr (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
)
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
⟨u, hu1, hu2, lt_of_lt_of_le ?_ (hsum.sum_le_tsum F fun p _ ↦ by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. positivity`positivity` solves goals of the form `0 ≤ x`, `0 < x` and `x ≠ 0`. The tactic works recursively
according to the syntax of the expression `x`, by attempting to prove subexpressions are
positive/nonnegative/nonzero and combining this into a final proof. This tactic either closes the
goal or fails.
For each subexpression `e`, `positivity` will try to:
* try `@[positivity]`-tagged extensions to recursively prove `e` is positive/nonnegative/nonzero
based on its subexpressions (see the `positivity` attribute for more details), or
* try the `norm_num` tactic to prove `e` is positive/nonnegative/nonzero, or
* try showing `e : t` is nonnegative because there is a `CanonicallyOrderedAdd t` instance, or
* use a local hypothesis of the form `0 ≤ e`, `0 < e` or `e ≠ 0`.
This tactic is extensible. See the `positivity` attribute documentation for more details.
* `positivity [t₁, …, tₙ]` first executes `have := t₁; …; have := tₙ` in the current goal,
then runs `positivity`. This is useful when `positivity` needs derived premises such as `0 < y`
for division/reciprocal, or `0 ≤ x` for real powers.
Examples:
```
example {a : ℤ} (ha : 3 < a) : 0 ≤ a ^ 3 + a := by positivity
example {a : ℤ} (ha : 1 < a) : 0 < |(3:ℤ) + a| := by positivity
example {b : ℤ} : 0 ≤ max (-3) (b ^ 2) := by positivity
example {a b c d : ℝ} (hab : 0 < a * b) (hb : 0 ≤ b) (hcd : c < d) :
0 < a ^ c + 1 / (d - c) := by
positivity [sub_pos_of_lt hcd, pos_of_mul_pos_left hab hb]
```
)⟩
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
lt_of_lt_of_le hu3 (le_of_eq (Finset.sum_congr rfl fun p _ ↦ ?_))
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[one_mul]
Partial sums are race sums. \sum_{k \le n} f_\chi(k) = S(n). The partial sums of
f_\chi are the race sums, so a bounded race is precisely the "bounded partial sums"
hypothesis the by-parts machinery needs.
Lean code for Lemma sum_range_fChi
-
⟲
theorem sum_range_fChi (n
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).kℕ= ((raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of `raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes `≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`, matching the convention of `Nat.primeCounting N` (# primes `≤ N`).nℕ: ℤInt : TypeThe integers. This type is special-cased by the compiler and overridden with an efficient implementation. The runtime has a special representation for `Int` that stores “small” signed numbers directly, while larger numbers use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)). A “small number” is an integer that can be encoded with one fewer bits than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit architectures).) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)theorem sum_range_fChi (n
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).kℕ= ((raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of `raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes `≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`, matching the convention of `Nat.primeCounting N` (# primes `≤ N`).nℕ: ℤInt : TypeThe integers. This type is special-cased by the compiler and overridden with an efficient implementation. The runtime has a special representation for `Int` that stores “small” signed numbers directly, while larger numbers use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)). A “small number” is an integer that can be encoded with one fewer bits than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit architectures).) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)
Unfold both sides: f_\chi(k) is \kappa(k) when k is prime and 0 otherwise, while
S(n) = \sum_{p \le n} \kappa(p) ranges over primes. Summing f_\chi over k \le n
discards the non-prime indices, whose terms are 0, leaving exactly the prime-filtered sum
that defines S(n). Unconditional bookkeeping — no summability is involved. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
unfold* `unfold id` unfolds all occurrences of definition `id` in the target.
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
* `unfold id at h` unfolds at the hypothesis `h`.
Definitions can be either global or local definitions.
For non-recursive global definitions, this tactic is identical to `delta`.
For recursive global definitions, it uses the "unfolding lemma" `id.eq_def`,
which is generated for each recursive definition, to unfold according to the recursive definition given by the user.
Only one level of unfolding is performed, in contrast to `simp only [id]`, which unfolds definition `id` recursively.
fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at
primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`),
so `Complex.bpSeries fChi` is the by-parts continuation of `layerA`
(via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[raceSum_eq_sum_raceKernelA362583.raceSum_eq_sum_raceKernel (N : ℕ) : raceSum N = ∑ p ∈ Finset.range (N + 1) with Nat.Prime p, raceKernel p`raceSum` is the sum of `raceKernel` over primes `≤ N` (definitional). , ← Finset.sum_filterFinset.sum_filter.{u_1, u_4} {ι : Type u_1} {M : Type u_4} {s : Finset ι} [AddCommMonoid M] (p : ι → Prop)
[DecidablePred p] (f : ι → M) : ∑ a ∈ s with p a, f a = ∑ a ∈ s, if p a then f a else 0]
push_cast`push_cast` rewrites the goal to move certain coercions (*casts*) inward, toward the leaf nodes.
This uses `norm_cast` lemmas in the forward direction.
For example, `↑(a + b)` will be written to `↑a + ↑b`.
- `push_cast` moves casts inward in the goal.
- `push_cast at h` moves casts inward in the hypothesis `h`.
It can be used with extra simp lemmas with, for example, `push_cast [Int.add_zero]`.
Example:
```lean
example (a b : Nat)
(h1 : ((a + b : Nat) : Int) = 10)
(h2 : ((a + b + 0 : Nat) : Int) = 10) :
((a + b : Nat) : Int) = 10 := by
/-
h1 : ↑(a + b) = 10
h2 : ↑(a + b + 0) = 10
⊢ ↑(a + b) = 10
-/
push_cast
/- Now
⊢ ↑a + ↑b = 10
-/
push_cast at h1
push_cast [Int.add_zero] at h2
/- Now
h1 h2 : ↑a + ↑b = 10
-/
exact h1
```
See also `norm_cast`.
rflThis tactic applies to a goal whose target has the form `x ~ x`,
where `~` is equality, heterogeneous equality or any relation that
has a reflexivity lemma tagged with the attribute @[refl].
Euler wiring. For \mathrm{Re}\, s > 1,
\exp\bigl(A(s) + B(s) + T(s)\bigr) \;=\; L(s, \chi),
where L is Mathlib's analytically continued Dirichlet L-function of \chi — an
entire function, since \chi is nonprincipal. From here on, "the L-function" always
means this continued object; its two properties used later are entirety (hence continuity at
1/2) and nonvanishing at s = 1.
Lean code for Theorem exp_layers_eq_LFunction
-
⟲
theorem exp_layers_eq_LFunction {s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs1 < s.re: 1ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : Complex.expComplex.exp (z : ℂ) : ℂThe complex exponential function, defined via its Taylor series(layerAA362583.layerA (s : ℂ) : ℂThe `k = 1` layer `A(s) = Σ_p χ(p) p^(-s)`.sℂ+ layerBA362583.layerB (s : ℂ) : ℂThe `k = 2` layer `B(s) = (1/2) Σ_{p odd} p^(-2s)`, with an `if`-mask at `p = 2` (`χ(2)² = 0`, `χ(p)² = 1` for odd `p`; see `χ_sq_eq_ite`, `tsum_term_two_eq_layerB`). Exponent convention: `-(2 * s)`.sℂ+ layerTA362583.layerT (s : ℂ) : ℂThe `k ≥ 3` layer `T(s) = Σ_p t_p(s)`.sℂ) = DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions. Note that this is not the same as `LSeries χ`: they agree in the convergence range, but `LSeries χ s` is defined to be `0` if `re s ≤ 1`.χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.sℂtheorem exp_layers_eq_LFunction {s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs1 < s.re: 1ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : Complex.expComplex.exp (z : ℂ) : ℂThe complex exponential function, defined via its Taylor series(layerAA362583.layerA (s : ℂ) : ℂThe `k = 1` layer `A(s) = Σ_p χ(p) p^(-s)`.sℂ+ layerBA362583.layerB (s : ℂ) : ℂThe `k = 2` layer `B(s) = (1/2) Σ_{p odd} p^(-2s)`, with an `if`-mask at `p = 2` (`χ(2)² = 0`, `χ(p)² = 1` for odd `p`; see `χ_sq_eq_ite`, `tsum_term_two_eq_layerB`). Exponent convention: `-(2 * s)`.sℂ+ layerTA362583.layerT (s : ℂ) : ℂThe `k ≥ 3` layer `T(s) = Σ_p t_p(s)`.sℂ) = DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions. Note that this is not the same as `LSeries χ`: they agree in the convergence range, but `LSeries χ s` is defined to be `0` if `re s ≤ 1`.χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.sℂ
By the layer split, A(s) + B(s) + T(s) = \sum_p -\operatorname{Log}(1 - \chi(p)\, p^{-s}).
Exponentiating, Mathlib's exponential form of the Euler product for a Dirichlet L-series
turns \exp of that prime sum into the L-series \sum_n \chi(n)\, n^{-s}; and on
\{\mathrm{Re}\, s > 1\} the analytically continued L-function agrees with its
L-series. Composing the three gives \exp(A + B + T) = L(s, \chi). That \chi is
nonprincipal (\chi \ne 1, witnessed at 3) is what makes the continued L entire.
\blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[← tsum_neg_log_eq_layersA362583.tsum_neg_log_eq_layers {s : ℂ} (hs : 1 < s.re) :
∑' (p : Nat.Primes), -Complex.log (1 - χ ↑↑p * ↑↑p ^ (-s)) = layerA s + layerB s + layerT s**Per-`p` split summed over primes**: for `Re s > 1`,
`Σ_p -log(1 - χ(p) p^(-s)) = A(s) + B(s) + T(s)`.
Proof: rewrite each term with the per-prime split `neg_log_split`, then split the sum
by linearity of three absolutely convergent series (`Summable.tsum_add` twice) and
identify the `k = 2` piece as `layerB` (`tsum_term_two_eq_layerB`).
No double-sum rearrangement is involved. hs1 < s.re, DirichletCharacter.LFunction_eq_LSeriesDirichletCharacter.LFunction_eq_LSeries {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) {s : ℂ} (hs : 1 < s.re) :
DirichletCharacter.LFunction χ s = LSeries (fun x => χ ↑x) sFor `1 < re s` the L-function of a Dirichlet character agrees with the sum of the naive Dirichlet
series.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. hs1 < s.re]
exact`exact e` closes the main goal if its target type matches that of `e`.
DirichletCharacter.LSeries_eulerProduct_exp_logDirichletCharacter.LSeries_eulerProduct_exp_log {N : ℕ} (χ : DirichletCharacter ℂ N) {s : ℂ} (hs : 1 < s.re) :
Complex.exp (∑' (p : Nat.Primes), -Complex.log (1 - χ ↑↑p * ↑↑p ^ (-s))) = LSeries (fun n => χ ↑n) sA variant of the Euler product for Dirichlet L-series. χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. hs1 < s.re
The k = 2 layer B. Since \chi(p)^2 = 1 for odd p and \chi(2)^2 = 0,
B(s) \;=\; \tfrac12 \sum_{p \text{ odd}} p^{-2s},
realized in Lean with an if-mask at p = 2. It is holomorphic on
\Omega = \{\mathrm{Re}\, s > 1/2\} by locally uniform convergence of its partial sums.
Lean code for Definition layerB
-
⟲
def layerB (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.def layerB (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.
⟲:= (1ℂ / 2ℂ) * ∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
(pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) = 2ℕ then`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
0ℂ else`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-(2ℂ * sℂ))
Vanishing boundary term. If the partial sums of f are bounded by C, then for
\mathrm{Re}\, s > 0 the Abel boundary term tends to zero:
\Bigl(\sum_{i \le N} f(i)\Bigr) N^{-s} \;\longrightarrow\; 0 \qquad (N \to \infty).
Lean code for Lemma tendsto_boundary_term
-
⌁
theorem tendsto_boundary_term
tendsto_boundary_term {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {s : ℂ} (hs : 0 < s.re) : Filter.Tendsto (fun N ↦ (∑ i ∈ Finset.range (N + 1), f i) * ↑N ^ (-s)) Filter.atTop (nhds 0)**The Abel boundary term vanishes**: if the partial sums of `f` are bounded by `C` and `0 < s.re`, then `(∑ i ≤ N, f i) · (N : ℂ) ^ (-s) → 0` as `N → ∞` — its norm is at most `C · N ^ (-s.re)`, which tends to `0`.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Cℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.), ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs0 < s.re: 0 <LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : Filter.TendstoFilter.Tendsto.{u_1, u_2} {α : Type u_1} {β : Type u_2} (f : α → β) (l₁ : Filter α) (l₂ : Filter β) : Prop`Filter.Tendsto` is the generic "limit of a function" predicate. `Tendsto f l₁ l₂` asserts that for every `l₂` neighborhood `a`, the `f`-preimage of `a` is an `l₁` neighborhood.(fun Nℕ↦ (∑ iℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.Nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂiℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.↑Nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).sℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).) Filter.atTopFilter.atTop.{u_3} {α : Type u_3} [Preorder α] : Filter α`atTop` is the filter representing the limit `→ ∞` on an ordered set. It is generated by the collection of up-sets `{b | a ≤ b}`. (The preorder need not have a top element for this to be well defined, and indeed is trivial when a top element `x` exists, i.e., it coincides with `pure x`.)(nhdsnhds.{u_3} {X : Type u_3} [TopologicalSpace X] (x : X) : Filter XA set is called a neighborhood of `x` if it contains an open set around `x`. The set of all neighborhoods of `x` forms a filter, the neighborhood filter at `x`, is here defined as the infimum over the principal filters of all open sets containing `x`.0)theorem tendsto_boundary_term
tendsto_boundary_term {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {s : ℂ} (hs : 0 < s.re) : Filter.Tendsto (fun N ↦ (∑ i ∈ Finset.range (N + 1), f i) * ↑N ^ (-s)) Filter.atTop (nhds 0)**The Abel boundary term vanishes**: if the partial sums of `f` are bounded by `C` and `0 < s.re`, then `(∑ i ≤ N, f i) · (N : ℂ) ^ (-s) → 0` as `N → ∞` — its norm is at most `C · N ^ (-s.re)`, which tends to `0`.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Cℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.), ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs0 < s.re: 0 <LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : Filter.TendstoFilter.Tendsto.{u_1, u_2} {α : Type u_1} {β : Type u_2} (f : α → β) (l₁ : Filter α) (l₂ : Filter β) : Prop`Filter.Tendsto` is the generic "limit of a function" predicate. `Tendsto f l₁ l₂` asserts that for every `l₂` neighborhood `a`, the `f`-preimage of `a` is an `l₁` neighborhood.(fun Nℕ↦ (∑ iℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.Nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂiℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.↑Nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).sℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).) Filter.atTopFilter.atTop.{u_3} {α : Type u_3} [Preorder α] : Filter α`atTop` is the filter representing the limit `→ ∞` on an ordered set. It is generated by the collection of up-sets `{b | a ≤ b}`. (The preorder need not have a top element for this to be well defined, and indeed is trivial when a top element `x` exists, i.e., it coincides with `pure x`.)(nhdsnhds.{u_3} {X : Type u_3} [TopologicalSpace X] (x : X) : Filter XA set is called a neighborhood of `x` if it contains an open set around `x`. The set of all neighborhoods of `x` forms a filter, the neighborhood filter at `x`, is here defined as the infimum over the principal filters of all open sets containing `x`.0)
Its norm is at most C\, N^{-\mathrm{Re}\, s}, which tends to 0 because
\mathrm{Re}\, s > 0. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
squeeze_zero_norm (a := fun N : ℕ ↦ C * (N : ℝ) ^ (-s.re)) (fun N ↦ ?_) ?_
· rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[norm_mul,
Complex.norm_natCast_cpow_of_re_ne_zero N
(by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[Complex.neg_re]; exact`exact e` closes the main goal if its target type matches that of `e`.
neg_ne_zero.2 hs.ne'),
Complex.neg_re]
exact`exact e` closes the main goal if its target type matches that of `e`.
mul_le_mul_of_nonneg_right (hC N) (Real.rpow_nonneg (Nat.cast_nonneg N) _)
· haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h := ((tendsto_rpow_neg_atTop hs).comp
(tendsto_natCast_atTop_atTop (R := ℝ))).const_mul C
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[mul_zero] atLocation specifications are used by many tactics that can operate on either the
hypotheses or the goal. It can have one of the forms:
* 'empty' is not actually present in this syntax, but most tactics use
`(location)?` matchers. It means to target the goal only.
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
* `at *`: target all hypotheses and the goal
h
exact`exact e` closes the main goal if its target type matches that of `e`.
h
Lower bound by the k = 2 layer. Under the bounded-race hypothesis, for real
\sigma > 1/2
B(\sigma) - C - c_T \;\le\; \mathrm{Re}\, G(\sigma).
Lean code for Lemma layerBReal_sub_le_re_contLog
-
⟲
theorem layerBReal_sub_le_re_contLog {C
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).kℕ‖ ≤ Cℝ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ1 / 2 < σ: 1ℝ/ 2ℝ< σℝ) : layerBRealA362583.layerBReal (σ : ℝ) : ℝReal companion of `layerB` (real `rpow`; agreement: `layerB_ofReal`).σℝ- Cℝ- cTA362583.cT : ℝExplicit uniform bound for `layerT` on `Re s ≥ 1/2`: `cT = (4/3) Σ_p p^(-3/2)` (a crude `κ ≤ 4` version of the `κ/3 · Σ n^(-3/2)` bound).≤ (contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi` is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3` layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}` (`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`).((σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)).reComplex.re (self : ℂ) : ℝThe real part of a complex number.theorem layerBReal_sub_le_re_contLog {C
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).kℕ‖ ≤ Cℝ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ1 / 2 < σ: 1ℝ/ 2ℝ< σℝ) : layerBRealA362583.layerBReal (σ : ℝ) : ℝReal companion of `layerB` (real `rpow`; agreement: `layerB_ofReal`).σℝ- Cℝ- cTA362583.cT : ℝExplicit uniform bound for `layerT` on `Re s ≥ 1/2`: `cT = (4/3) Σ_p p^(-3/2)` (a crude `κ ≤ 4` version of the `κ/3 · Σ n^(-3/2)` bound).≤ (contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi` is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3` layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}` (`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`).((σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)).reComplex.re (self : ℂ) : ℝThe real part of a complex number.
On the real axis the three pieces of G = \tilde A_{f_\chi} + B + T are real, so
\mathrm{Re}\, G(\sigma) = \mathrm{Re}\, \tilde A_{f_\chi}(\sigma) + B(\sigma) + T(\sigma).
The by-parts term is bounded below by the real-segment estimate,
\mathrm{Re}\, \tilde A_{f_\chi}(\sigma) \ge -\|\tilde A_{f_\chi}(\sigma)\| \ge -C (partial
sums of f_\chi bounded by C, support above n = 2), and the k \ge 3 layer by its
uniform bound T(\sigma) \ge -|T(\sigma)| \ge -c_T. Adding these gives
\mathrm{Re}\, G(\sigma) \ge B(\sigma) - C - c_T. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hre(contLog ↑σ).re = (bpSeries fChi ↑σ).re + layerBReal σ + layerTReal σ : (contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi`
is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3`
layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}`
(`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`). ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )).reComplex.re (self : ℂ) : ℝThe real part of a complex number. =
(bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`:
`bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`.
If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this
series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it
agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re`
(`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series
to the right half-plane. This is the analytic-continuation object `Ã` of the proof. fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at
primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`),
so `Complex.bpSeries fChi` is the by-parts continuation of `layerA`
(via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`). ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )).reComplex.re (self : ℂ) : ℝThe real part of a complex number. + layerBRealA362583.layerBReal (σ : ℝ) : ℝReal companion of `layerB` (real `rpow`; agreement: `layerB_ofReal`). σℝ + layerTRealA362583.layerTReal (σ : ℝ) : ℝReal companion of `layerT` (agreement: `layerT_ofReal`). σℝ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi`
is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3`
layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}`
(`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`). , Complex.add_reComplex.add_re (z w : ℂ) : (z + w).re = z.re + w.re, layerB_reA362583.layerB_re (σ : ℝ) : (layerB ↑σ).re = layerBReal σReal part of `layerB` on the real axis. , layerT_reA362583.layerT_re (σ : ℝ) : (layerT ↑σ).re = layerTReal σReal part of `layerT` on the real axis. ]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hσ00 ≤ σ : (0ℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) ≤ σℝ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hbp‖bpSeries fChi ↑σ‖ ≤ C : ‖bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`:
`bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`.
If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this
series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it
agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re`
(`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series
to the right half-plane. This is the analytic-continuation object `Ã` of the proof. fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at
primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`),
so `Complex.bpSeries fChi` is the by-parts continuation of `layerA`
(via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`). ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )‖ ≤ Cℝ :=
norm_bpSeries_le_constnorm_bpSeries_le_const {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {n₀ : ℕ}
(hn₀ : 1 ≤ n₀) (hvanish : ∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0) {σ : ℝ} (hσ : 0 ≤ σ) : ‖bpSeries f ↑σ‖ ≤ CConvenience form of `norm_bpSeries_le`: under the same hypotheses,
`‖bpSeries f σ‖ ≤ C` (since `(n₀ : ℝ) ^ (-σ) ≤ 1`). (n₀ := 2ℕ) hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C one_le_twoone_le_two.{u_1} {α : Type u_1} [AddMonoidWithOne α] [LE α] [ZeroLEOneClass α] [AddLeftMono α] : 1 ≤ 2 sum_range_fChi_vanishA362583.sum_range_fChi_vanish (n : ℕ) : n < 2 → ∑ k ∈ Finset.range (n + 1), fChi k = 0Side condition: the partial sums of `fChi` vanish below `n₀ = 2`
(`fChi 0 = fChi 1 = 0`; feeds `norm_bpSeries_le_const`). hσ00 ≤ σ
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hbpre-C ≤ (bpSeries fChi ↑σ).re : -Cℝ ≤ (bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`:
`bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`.
If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this
series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it
agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re`
(`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series
to the right half-plane. This is the analytic-continuation object `Ã` of the proof. fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at
primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`),
so `Complex.bpSeries fChi` is the by-parts continuation of `layerA`
(via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`). ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )).reComplex.re (self : ℂ) : ℝThe real part of a complex number. :=
(abs_leabs_le.{u_1} {G : Type u_1} [AddCommGroup G] [LinearOrder G] [IsOrderedAddMonoid G] {a b : G} : |a| ≤ b ↔ -b ≤ a ∧ a ≤ b.mpIff.mp {a b : Prop} (self : a ↔ b) : a → bModus ponens for if and only if. If `a ↔ b` and `a`, then `b`. ((Complex.abs_re_le_normComplex.abs_re_le_norm (z : ℂ) : |z.re| ≤ ‖z‖ _).transLE.le.trans.{u_1} {α : Type u_1} [Preorder α] {a b c : α} : a ≤ b → b ≤ c → a ≤ c**Alias** of `le_trans`. hbp‖bpSeries fChi ↑σ‖ ≤ C)).1And.left {a b : Prop} (self : a ∧ b) : aExtract the left conjunct from a conjunction. `h : a ∧ b` then
`h.left`, also notated as `h.1`, is a proof of `a`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hT-cT ≤ layerTReal σ : -cTA362583.cT : ℝExplicit uniform bound for `layerT` on `Re s ≥ 1/2`:
`cT = (4/3) Σ_p p^(-3/2)` (a crude `κ ≤ 4` version of the `κ/3 · Σ n^(-3/2)` bound). ≤ layerTRealA362583.layerTReal (σ : ℝ) : ℝReal companion of `layerT` (agreement: `layerT_ofReal`). σℝ := (abs_leabs_le.{u_1} {G : Type u_1} [AddCommGroup G] [LinearOrder G] [IsOrderedAddMonoid G] {a b : G} : |a| ≤ b ↔ -b ≤ a ∧ a ≤ b.mpIff.mp {a b : Prop} (self : a ↔ b) : a → bModus ponens for if and only if. If `a ↔ b` and `a`, then `b`. (abs_layerTReal_leA362583.abs_layerTReal_le {σ : ℝ} (hσ : 1 / 2 ≤ σ) : |layerTReal σ| ≤ cTReal-companion bound `|layerTReal σ| ≤ cT` for real `σ ≥ 1/2`. hσ1 / 2 < σ.leLT.lt.le.{u_1} {α : Type u_1} [Preorder α] {a b : α} (hab : a < b) : a ≤ b**Alias** of `le_of_lt`.)).1And.left {a b : Prop} (self : a ∧ b) : aExtract the left conjunct from a conjunction. `h : a ∧ b` then
`h.left`, also notated as `h.1`, is a proof of `a`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hre(contLog ↑σ).re = (bpSeries fChi ↑σ).re + layerBReal σ + layerTReal σ]
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
Summable majorant. For C, R \ge 0 and \delta > 0 the sequence
n \longmapsto \begin{cases} C & n = 0, \\ C\,R\, n^{-\delta - 1} & n \ge 1 \end{cases}
is summable — the Weierstrass majorant for the by-parts terms on a box
\{\mathrm{Re}\, s > \delta\} \cap B(0, R).
Lean code for Lemma summable_bpSeries_boxBound
-
≈
theorem summable_bpSeries_boxBound {R
ℝδℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC00 ≤ C: 0ℝ≤ Cℝ) (hR00 ≤ R: 0ℝ≤ Rℝ) (hδ0 < δ: 0ℝ< δℝ) : SummableSummable.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (f : β → α) (L : SummationFilter β := SummationFilter.unconditional β) : Prop`Summable f` means that `f` has some (infinite) sum with respect to `L`. Use `tsum` to get the value.(fun nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.↦ if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.nℕ= 0ℕthen`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.Cℝelse`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.Cℝ* Rℝ* (nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) ^ (-δℝ- 1ℝ))theorem summable_bpSeries_boxBound {R
ℝδℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC00 ≤ C: 0ℝ≤ Cℝ) (hR00 ≤ R: 0ℝ≤ Rℝ) (hδ0 < δ: 0ℝ< δℝ) : SummableSummable.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (f : β → α) (L : SummationFilter β := SummationFilter.unconditional β) : Prop`Summable f` means that `f` has some (infinite) sum with respect to `L`. Use `tsum` to get the value.(fun nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.↦ if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.nℕ= 0ℕthen`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.Cℝelse`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.Cℝ* Rℝ* (nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) ^ (-δℝ- 1ℝ))
Adjusting the single n = 0 term does not affect summability, and
\sum_{n \ge 1} n^{-\delta - 1} converges because \delta + 1 > 1. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
Summable.of_norm_bounded_eventually_nat
(g := fun n : ℕ ↦ C * R * (n : ℝ) ^ (-δ - 1))
((Real.summable_nat_rpow.2 (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
)).mul_left _) ?_
filter_upwards`filter_upwards [h₁, ⋯, hₙ]` replaces a goal of the form `s ∈ f` and terms
`h₁ : t₁ ∈ f, ⋯, hₙ : tₙ ∈ f` with `∀ x, x ∈ t₁ → ⋯ → x ∈ tₙ → x ∈ s`.
The list is an optional parameter, `[]` being its default value.
`filter_upwards [h₁, ⋯, hₙ] with a₁ a₂ ⋯ aₖ` is a short form for
`{ filter_upwards [h₁, ⋯, hₙ], intro a₁ a₂ ⋯ aₖ }`.
`filter_upwards [h₁, ⋯, hₙ] using e` is a short form for
`{ filter_upwards [h1, ⋯, hn], exact e }`.
Combining both shortcuts is done by writing `filter_upwards [h₁, ⋯, hₙ] with a₁ a₂ ⋯ aₖ using e`.
Note that in this case, the `aᵢ` terms can be used in `e`.
[eventually_ge_atTop 1] with`filter_upwards [h₁, ⋯, hₙ]` replaces a goal of the form `s ∈ f` and terms
`h₁ : t₁ ∈ f, ⋯, hₙ : tₙ ∈ f` with `∀ x, x ∈ t₁ → ⋯ → x ∈ tₙ → x ∈ s`.
The list is an optional parameter, `[]` being its default value.
`filter_upwards [h₁, ⋯, hₙ] with a₁ a₂ ⋯ aₖ` is a short form for
`{ filter_upwards [h₁, ⋯, hₙ], intro a₁ a₂ ⋯ aₖ }`.
`filter_upwards [h₁, ⋯, hₙ] using e` is a short form for
`{ filter_upwards [h1, ⋯, hn], exact e }`.
Combining both shortcuts is done by writing `filter_upwards [h₁, ⋯, hₙ] with a₁ a₂ ⋯ aₖ using e`.
Note that in this case, the `aᵢ` terms can be used in `e`.
n hn
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[if_neg (Nat.one_le_iff_ne_zero.mp hn), Real.norm_eq_abs, abs_of_nonneg
(mul_nonneg (mul_nonneg hC0 hR0) (Real.rpow_nonneg (Nat.cast_nonneg n) _))]
Holomorphy on a box. If the partial sums of f are bounded by C, then for \delta > 0
and R \ge 1 the by-parts series \tilde A_f is holomorphic on the box
\{\mathrm{Re}\, s > \delta\} \cap B(0, R).
Lean code for Theorem differentiableOn_bpSeries_box
-
⟲
theorem differentiableOn_bpSeries_box (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) {δℝRℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hδ0 < δ: 0ℝ< δℝ) (hR1 ≤ R: 1ℝ≤ Rℝ) : DifferentiableOnDifferentiableOn.{u_1, u_2, u_3} (𝕜 : Type u_1) [NontriviallyNormedField 𝕜] {E : Type u_2} [AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F] (f : E → F) (s : Set E) : Prop`DifferentiableOn 𝕜 f s` means that `f` is differentiable within `s` at any point of `s`.ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.(bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂ) ({sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.| δℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.} ∩ Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε`0ℂRℝ)theorem differentiableOn_bpSeries_box (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) {δℝRℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hδ0 < δ: 0ℝ< δℝ) (hR1 ≤ R: 1ℝ≤ Rℝ) : DifferentiableOnDifferentiableOn.{u_1, u_2, u_3} (𝕜 : Type u_1) [NontriviallyNormedField 𝕜] {E : Type u_2} [AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F] (f : E → F) (s : Set E) : Prop`DifferentiableOn 𝕜 f s` means that `f` is differentiable within `s` at any point of `s`.ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.(bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂ) ({sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.| δℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.} ∩ Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε`0ℂRℝ)
Each term is holomorphic on the box, and their norms are dominated there by a single summable
majorant, so the Weierstrass M-test for series of holomorphic functions (the
locally-uniform-limits theorem) applies. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hC00 ≤ C : 0ℝ ≤ Cℝ := (norm_nonnegnorm_nonneg.{u_5} {E : Type u_5} [SeminormedAddGroup E] (a : E) : 0 ≤ ‖a‖ _).transLE.le.trans.{u_1} {α : Type u_1} [Preorder α] {a b c : α} : a ≤ b → b ≤ c → a ≤ c**Alias** of `le_trans`. (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C 0ℕ)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hR00 ≤ R : 0ℝ ≤ Rℝ := zero_le_onezero_le_one.{u_1} {α : Type u_1} [Zero α] [One α] [LE α] [ZeroLEOneClass α] : 0 ≤ 1`zero_le_one` with the type argument implicit. .transLE.le.trans.{u_1} {α : Type u_1} [Preorder α] {a b c : α} : a ≤ b → b ≤ c → a ≤ c**Alias** of `le_trans`. hR1 ≤ R
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hVopenIsOpen ({s | δ < s.re} ∩ Metric.ball 0 R) : IsOpenIsOpen.{u} {X : Type u} [TopologicalSpace X] : Set X → Prop`IsOpen s` means that `s` is open in the ambient topological space on `X` ({sℂ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. | δℝ < sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number. } ∩ Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε` 0ℂ Rℝ) :=
(isOpen_ltisOpen_lt.{u, v} {α : Type u} {β : Type v} [TopologicalSpace α] [LinearOrder α] [OrderClosedTopology α]
[TopologicalSpace β] {f g : β → α} (hf : Continuous f) (hg : Continuous g) : IsOpen {b | f b < g b} continuous_constcontinuous_const.{u_1, u_2} {X : Type u_1} {Y : Type u_2} [TopologicalSpace X] [TopologicalSpace Y] {y : Y} :
Continuous fun x => y Complex.continuous_reComplex.continuous_re : Continuous Complex.re).interIsOpen.inter.{u} {X : Type u} [TopologicalSpace X] {s t : Set X} (hs : IsOpen s) (ht : IsOpen t) : IsOpen (s ∩ t) Metric.isOpen_ballMetric.isOpen_ball.{u} {α : Type u} [PseudoMetricSpace α] {x : α} {ε : ℝ} : IsOpen (Metric.ball x ε)
exact`exact e` closes the main goal if its target type matches that of `e`.
Complex.differentiableOn_tsum_of_summable_normComplex.differentiableOn_tsum_of_summable_norm.{u_1, u_2} {E : Type u_1} {ι : Type u_2} [NormedAddCommGroup E]
[NormedSpace ℂ E] {U : Set ℂ} {F : ι → ℂ → E} [CompleteSpace E] {u : ι → ℝ} (hu : Summable u)
(hf : ∀ (i : ι), DifferentiableOn ℂ (F i) U) (hU : IsOpen U) (hF_le : ∀ (i : ι), ∀ w ∈ U, ‖F i w‖ ≤ u i) :
DifferentiableOn ℂ (fun w => ∑' (i : ι), F i w) UIf the terms in the sum `∑' (i : ι), F i` are uniformly bounded on `U` by a
summable function, and each term in the sum is differentiable on `U`, then so is the sum.
(summable_bpSeries_boxBoundsummable_bpSeries_boxBound {C R δ : ℝ} (hC0 : 0 ≤ C) (hR0 : 0 ≤ R) (hδ : 0 < δ) :
Summable fun n => if n = 0 then C else C * R * ↑n ^ (-δ - 1)**Summable majorant for the box `M`-test**: on `{s | δ < s.re} ∩ ball 0 R` (with `δ > 0`,
`0 ≤ R`) the by-parts terms are dominated by `n ↦ if n = 0 then C else C · R · n ^ (-δ - 1)`,
which is summable because `-δ - 1 < -1`. hC00 ≤ C hR00 ≤ R hδ0 < δ)
(fun nℕ ↦ differentiableOn_bpSeries_termdifferentiableOn_bpSeries_term {f : ℕ → ℂ} {R δ : ℝ} (hδ : 0 < δ) (n : ℕ) :
DifferentiableOn ℂ (fun s => (∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-s) - (↑n + 1) ^ (-s)))
({s | δ < s.re} ∩ Metric.ball 0 R)**Per-term holomorphy on the box**: each by-parts term
`(∑ k ≤ n, f k) · ((n : ℂ) ^ (-s) - (n + 1) ^ (-s))` is holomorphic on
`{s | δ < s.re} ∩ ball 0 R` — a constant when `n = 0`, a difference of complex powers
otherwise. (R := Rℝ) hδ0 < δ nℕ) hVopenIsOpen ({s | δ < s.re} ∩ Metric.ball 0 R)
(fun nℕ wℂ hww ∈ {s | δ < s.re} ∩ Metric.ball 0 R ↦ norm_bpSeries_term_le_boxBoundnorm_bpSeries_term_le_boxBound {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {R δ : ℝ}
(hR0 : 0 ≤ R) (hδ : 0 < δ) (n : ℕ) (w : ℂ) (hw : w ∈ {s | δ < s.re} ∩ Metric.ball 0 R) :
‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-w) - (↑n + 1) ^ (-w))‖ ≤ if n = 0 then C else C * R * ↑n ^ (-δ - 1)**Uniform `M`-test bound on the box**: if the partial sums of `f` are bounded by `C`,
then on `{s | δ < s.re} ∩ ball 0 R` each by-parts term satisfies
`‖term n w‖ ≤ if n = 0 then C else C · R · n ^ (-δ - 1)`. hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C hR00 ≤ R hδ0 < δ nℕ wℂ hww ∈ {s | δ < s.re} ∩ Metric.ball 0 R)
The prime race constant \varrho is the sum of the series
\varrho \;=\; \sum_{k \ge 0} \frac{b_k}{2^{\,k+1}} \;=\; 0.7004001\ldots,
which converges absolutely by comparison with the geometric series. Since each b_k is
0 or 1, the series is exactly the place-value reading of the binary numeral
0.b_0 b_1 b_2 \ldots_2 — and these are genuinely the binary digits of \varrho:
infinitely many b_k are 0 (Dirichlet's theorem), so the digit string is not
eventually all 1s and no carry ambiguity arises. Reading successive digit prefixes as
binary integers recovers the OEIS sequence A362583, which is why we call \varrho the
A362583 constant. The theorem of this blueprint is that \varrho is irrational.
Lean code for Definition ϱ
-
⟲
def ϱ : ℝ
Real : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.def ϱ : ℝ
Real : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.
⟲:= ∑' kℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
, (bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`.
The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1`
(primes `3, 5, 7, 11, 13, 17, 19, 23`). kℕ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) / 2ℝ ^ (kℕ + 1ℕ)
The k = 1 layer. A(s) = \sum_p \chi(p)\, p^{-s}, absolutely convergent for
\mathrm{Re}\, s > 1. This is the layer that carries the race: everything the analytic
argument ever learns about the distribution of the bits flows through A.
Lean code for Definition layerA
-
⟲
def layerA (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.def layerA (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.
⟲:= ∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)
Logarithm series. For \|z\| < 1,
\sum_{k \ge 1} \frac{z^k}{k} \;=\; -\operatorname{Log}(1 - z)
with the principal complex logarithm, in summed (HasSum) form. All uses have
\|z_p\| \le 2^{-\sigma} < 1/2 since only \sigma > 1 is ever consumed.
Lean code for Lemma hasSum_neg_log_one_sub
-
⟲
theorem hasSum_neg_log_one_sub {z
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hz‖z‖ < 1: ‖zℂ‖ < 1ℝ) : HasSumHasSum.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (f : β → α) (a : α) (L : SummationFilter β := SummationFilter.unconditional β) : Prop`HasSum f a L` means that the (potentially infinite) sum of the `f b` for `b : β` converges to `a` along the SummationFilter `L`. By default `L` is the `unconditional` one, corresponding to the limit of all finite sets towards the entire type. So we take the sum over bigger and bigger finite sets. This sum operation is invariant under permuting the terms (while sums for more general summation filters usually are not). This is based on Mario Carneiro's [infinite sum `df-tsms` in Metamath](http://us.metamath.org/mpeuni/df-tsms.html). In particular, the function `ℕ → ℝ` sending `n` to `(-1) ^ n / (n + 1)` does not have a sum for this definition, although it is summable for the `conditional` summation filter that takes limits of sums over `n ∈ {0, ..., X}` as `X → ∞`. However, a series which is *absolutely* convergent with respect to the conditional summation filter is in fact unconditionally summable. For the definition and many statements, `α` does not need to be a topological additive monoid, only an additive monoid with a topology (i.e. the addition is not assumed to be continuous). We only add this assumption later, for the lemmas where it is relevant.(fun kℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.↦ zℂ^ kℕ/ kℕ) (-Complex.logComplex.log (x : ℂ) : ℂInverse of the `exp` function. Returns values such that `(log x).im > - π` and `(log x).im ≤ π`. `log 0 = 0`(1ℂ- zℂ))theorem hasSum_neg_log_one_sub {z
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hz‖z‖ < 1: ‖zℂ‖ < 1ℝ) : HasSumHasSum.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (f : β → α) (a : α) (L : SummationFilter β := SummationFilter.unconditional β) : Prop`HasSum f a L` means that the (potentially infinite) sum of the `f b` for `b : β` converges to `a` along the SummationFilter `L`. By default `L` is the `unconditional` one, corresponding to the limit of all finite sets towards the entire type. So we take the sum over bigger and bigger finite sets. This sum operation is invariant under permuting the terms (while sums for more general summation filters usually are not). This is based on Mario Carneiro's [infinite sum `df-tsms` in Metamath](http://us.metamath.org/mpeuni/df-tsms.html). In particular, the function `ℕ → ℝ` sending `n` to `(-1) ^ n / (n + 1)` does not have a sum for this definition, although it is summable for the `conditional` summation filter that takes limits of sums over `n ∈ {0, ..., X}` as `X → ∞`. However, a series which is *absolutely* convergent with respect to the conditional summation filter is in fact unconditionally summable. For the definition and many statements, `α` does not need to be a topological additive monoid, only an additive monoid with a topology (i.e. the addition is not assumed to be continuous). We only add this assumption later, for the lemmas where it is relevant.(fun kℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.↦ zℂ^ kℕ/ kℕ) (-Complex.logComplex.log (x : ℂ) : ℂInverse of the `exp` function. Returns values such that `(log x).im > - π` and `(log x).im ≤ π`. `log 0 = 0`(1ℂ- zℂ))
This is Mathlib's Taylor expansion of -\operatorname{Log}(1 - z), quoted verbatim (the
k = 0 term is z^0/0 = 0 by Lean's convention, so the sum may be read from k \ge 1).
The branch of the logarithm — the one genuine analytic subtlety — is pinned inside Mathlib's
own proof, so no separate branch-pinning argument is needed here. \blacksquare
⟲Complex.hasSum_taylorSeries_neg_logComplex.hasSum_taylorSeries_neg_log {z : ℂ} (hz : ‖z‖ < 1) : HasSum (fun n => z ^ n / ↑n) (-Complex.log (1 - z))The series `∑ z^n/n` converges to `-log (1-z)` on the open unit disk. hz‖z‖ < 1
The race kernel. The elementary integer function \kappa(n) = +1 if n \equiv 1 \pmod 4,
-1 if n \equiv 3 \pmod 4, and 0 otherwise — definitionally the integrand of the race
sum, so S(N) = \sum_{p \le N} \kappa(p).
Lean code for Definition raceKernel
-
⟲
def raceKernel (n
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℤInt : TypeThe integers. This type is special-cased by the compiler and overridden with an efficient implementation. The runtime has a special representation for `Int` that stores “small” signed numbers directly, while larger numbers use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)). A “small number” is an integer that can be encoded with one fewer bits than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit architectures).def raceKernel (n
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℤInt : TypeThe integers. This type is special-cased by the compiler and overridden with an efficient implementation. The runtime has a special representation for `Int` that stores “small” signed numbers directly, while larger numbers use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)). A “small number” is an integer that can be encoded with one fewer bits than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit architectures).
⟲:= if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
nℕ % 4ℕ = 1ℕ then`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
1ℤ else`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
nℕ % 4ℕ = 3ℕ then`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
-1ℤ else`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
0ℤ
Holomorphy. If the partial sums satisfy
\bigl\| \sum_{k \le n} f(k) \bigr\| \le C for all n, then \tilde A_f is holomorphic
on the open right half-plane \{\mathrm{Re}\, s > 0\}.
Lean code for Theorem differentiableOn_bpSeries
-
⟲
theorem differentiableOn_bpSeries (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) : DifferentiableOnDifferentiableOn.{u_1, u_2, u_3} (𝕜 : Type u_1) [NontriviallyNormedField 𝕜] {E : Type u_2} [AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F] (f : E → F) (s : Set E) : Prop`DifferentiableOn 𝕜 f s` means that `f` is differentiable within `s` at any point of `s`.ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.(bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂ) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.| 0ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.}theorem differentiableOn_bpSeries (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) : DifferentiableOnDifferentiableOn.{u_1, u_2, u_3} (𝕜 : Type u_1) [NontriviallyNormedField 𝕜] {E : Type u_2} [AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F] (f : E → F) (s : Set E) : Prop`DifferentiableOn 𝕜 f s` means that `f` is differentiable within `s` at any point of `s`.ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.(bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂ) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.| 0ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.}
Holomorphy is local. Around any s_0 with \mathrm{Re}\, s_0 > 0, choose
0 < \delta < \mathrm{Re}\, s_0 and R \ge 1 with \|s_0\| < R; the previous theorem makes
\tilde A_f holomorphic on the box \{\mathrm{Re}\, s > \delta\} \cap B(0, R), hence at
s_0. As s_0 was arbitrary, \tilde A_f is holomorphic on all of
\{\mathrm{Re}\, s > 0\}. Consumers needing a smaller half-plane, such as
\Omega = \{\mathrm{Re}\, s > 1/2\} downstream, simply restrict. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
introIntroduces one or more hypotheses, optionally naming and/or pattern-matching them.
For each hypothesis to be introduced, the remaining main goal's target type must
be a `let` or function type.
* `intro` by itself introduces one anonymous hypothesis, which can be accessed
by e.g. `assumption`. It is equivalent to `intro _`.
* `intro x y` introduces two hypotheses and names them. Individual hypotheses
can be anonymized via `_`, given a type ascription, or matched against a pattern:
```lean
-- ... ⊢ α × β → ...
intro (a, b)
-- ..., a : α, b : β ⊢ ...
```
* `intro rfl` is short for `intro h; subst h`, if `h` is an equality where the left-hand or right-hand side
is a variable.
* Alternatively, `intro` can be combined with pattern matching much like `fun`:
```lean
intro
| n + 1, 0 => tac
| ...
```
s₀ℂ hs₀s₀ ∈ {s | 0 < s.re}
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hs₀re0 < s₀.re : 0ℝ < s₀ℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number. := hs₀s₀ ∈ {s | 0 < s.re}
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨δℝ, hδ0 < δ, hδs₀δ < s₀.re⟩ : ∃ δℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. , 0ℝ < δℝ ∧ δℝ < s₀ℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number. :=
⟨Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. s₀ℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number. / 2ℝ,Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. half_poshalf_pos.{u_2} {α : Type u_2} [Semifield α] [PartialOrder α] [PosMulReflectLT α] {a : α} [IsStrictOrderedRing α]
(h : 0 < a) : 0 < a / 2 hs₀re0 < s₀.re,Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. half_lt_selfhalf_lt_self.{u_2} {α : Type u_2} [Semifield α] [PartialOrder α] [PosMulReflectLT α] {a : α} [IsStrictOrderedRing α] :
0 < a → a / 2 < a**Alias** of the reverse direction of `half_lt_self_iff`. hs₀re0 < s₀.re⟩Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`.
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨Rℝ, hR1 ≤ R, hs₀R‖s₀‖ < R⟩ : ∃ Rℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. , 1ℝ ≤ Rℝ ∧ ‖s₀ℂ‖ < Rℝ :=
⟨Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. ‖s₀ℂ‖ + 1ℝ,Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. le_add_of_nonneg_leftle_add_of_nonneg_left.{u_1} {α : Type u_1} [AddZeroClass α] [LE α] [AddRightMono α] {a b : α} (h : 0 ≤ b) : a ≤ b + a (norm_nonnegnorm_nonneg.{u_5} {E : Type u_5} [SeminormedAddGroup E] (a : E) : 0 ≤ ‖a‖ _),Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. lt_add_onelt_add_one.{u_1} {α : Type u_1} [One α] [AddZeroClass α] [PartialOrder α] [ZeroLEOneClass α] [NeZero 1]
[AddLeftStrictMono α] (a : α) : a < a + 1 _⟩Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hVopenIsOpen ({s | δ < s.re} ∩ Metric.ball 0 R) : IsOpenIsOpen.{u} {X : Type u} [TopologicalSpace X] : Set X → Prop`IsOpen s` means that `s` is open in the ambient topological space on `X` ({sℂ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. | δℝ < sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number. } ∩ Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε` 0ℂ Rℝ) :=
(isOpen_ltisOpen_lt.{u, v} {α : Type u} {β : Type v} [TopologicalSpace α] [LinearOrder α] [OrderClosedTopology α]
[TopologicalSpace β] {f g : β → α} (hf : Continuous f) (hg : Continuous g) : IsOpen {b | f b < g b} continuous_constcontinuous_const.{u_1, u_2} {X : Type u_1} {Y : Type u_2} [TopologicalSpace X] [TopologicalSpace Y] {y : Y} :
Continuous fun x => y Complex.continuous_reComplex.continuous_re : Continuous Complex.re).interIsOpen.inter.{u} {X : Type u} [TopologicalSpace X] {s t : Set X} (hs : IsOpen s) (ht : IsOpen t) : IsOpen (s ∩ t) Metric.isOpen_ballMetric.isOpen_ball.{u} {α : Type u} [PseudoMetricSpace α] {x : α} {ε : ℝ} : IsOpen (Metric.ball x ε)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hs₀Vs₀ ∈ {s | δ < s.re} ∩ Metric.ball 0 R : s₀ℂ ∈ {sℂ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. | δℝ < sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number. } ∩ Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε` 0ℂ Rℝ :=
⟨And.intro {a b : Prop} (left : a) (right : b) : a ∧ b`And.intro : a → b → a ∧ b` is the constructor for the And operation. hδs₀δ < s₀.re,And.intro {a b : Prop} (left : a) (right : b) : a ∧ b`And.intro : a → b → a ∧ b` is the constructor for the And operation. by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[Metric.mem_ballMetric.mem_ball.{u} {α : Type u} [PseudoMetricSpace α] {x y : α} {ε : ℝ} : y ∈ Metric.ball x ε ↔ dist y x < ε, dist_zero_rightdist_zero_right.{u_5} {E : Type u_5} [SeminormedAddGroup E] (a : E) : dist a 0 = ‖a‖]; exact`exact e` closes the main goal if its target type matches that of `e`.
hs₀R‖s₀‖ < R⟩And.intro {a b : Prop} (left : a) (right : b) : a ∧ b`And.intro : a → b → a ∧ b` is the constructor for the And operation.
exact`exact e` closes the main goal if its target type matches that of `e`.
(((differentiableOn_bpSeries_boxdifferentiableOn_bpSeries_box {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {δ R : ℝ}
(hδ : 0 < δ) (hR : 1 ≤ R) : DifferentiableOn ℂ (bpSeries f) ({s | δ < s.re} ∩ Metric.ball 0 R)**Bounded partial sums give a holomorphic by-parts series on a box**: the Weierstrass
`M`-test packaged from `summable_bpSeries_boxBound`, `differentiableOn_bpSeries_term`, and
`norm_bpSeries_term_le_boxBound`. A box is used because no summable bound is uniform on the
whole half-plane `{0 < s.re}`; `differentiableOn_bpSeries` glues the boxes together. hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C hδ0 < δ hR1 ≤ R) s₀ℂ hs₀Vs₀ ∈ {s | δ < s.re} ∩ Metric.ball 0 R).differentiableAtDifferentiableWithinAt.differentiableAt.{u_1, u_2, u_3} {𝕜 : Type u_1} [NontriviallyNormedField 𝕜] {E : Type u_2}
[AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F]
{f : E → F} {x : E} {s : Set E} (h : DifferentiableWithinAt 𝕜 f s x) (hs : s ∈ nhds x) : DifferentiableAt 𝕜 f x
(hVopenIsOpen ({s | δ < s.re} ∩ Metric.ball 0 R).mem_nhdsIsOpen.mem_nhds.{u} {X : Type u} [TopologicalSpace X] {x : X} {s : Set X} (hs : IsOpen s) (hx : x ∈ s) : s ∈ nhds x hs₀Vs₀ ∈ {s | δ < s.re} ∩ Metric.ball 0 R)).differentiableWithinAtDifferentiableAt.differentiableWithinAt.{u_1, u_2, u_3} {𝕜 : Type u_1} [NontriviallyNormedField 𝕜] {E : Type u_2}
[AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F]
{f : E → F} {x : E} {s : Set E} (h : DifferentiableAt 𝕜 f x) : DifferentiableWithinAt 𝕜 f s x
Main analytic theorem. There are no constants c, C with
\bigl| S(N) - c\,\pi(N) \bigr| \;\le\; C \qquad \text{for all } N.
The mod-4 prime race is never linear in the prime count. The statement is meaningful
independently of the prime race constant \varrho, quantifies only over elementary objects,
and is the analytic core underlying the irrationality theorem.
Lean code for Theorem raceSum_not_linear
-
⟲
theorem raceSum_not_linear : ¬ ∃ (c
ℝCℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.), ∀ Nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., |(raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of `raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes `≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`, matching the convention of `Nat.primeCounting N` (# primes `≤ N`).Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) - cℝ* (Nat.primeCountingNat.primeCounting (n : ℕ) : ℕThe prime counting function: Returns the number of primes less than or equal to the input. With `open scoped Nat.Prime`, this has notation `π`.Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.)| ≤ Cℝtheorem raceSum_not_linear : ¬ ∃ (c
ℝCℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.), ∀ Nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., |(raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of `raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes `≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`, matching the convention of `Nat.primeCounting N` (# primes `≤ N`).Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) - cℝ* (Nat.primeCountingNat.primeCounting (n : ℕ) : ℕThe prime counting function: Returns the number of primes less than or equal to the input. With `open scoped Nat.Prime`, this has notation `π`.Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.)| ≤ Cℝ
Suppose such c, C exist. The forcing theorem gives c = 0, so |S(N)| \le C; the
partial sums of f_\chi are then bounded by C, and the identity theorem applies.
(Single-point form.) L(\cdot, \chi) is entire, hence continuous at 1/2: there
are M_0 := \|L(1/2, \chi)\| + 1 and \delta_0 > 0 with \|L(s, \chi)\| < M_0
whenever \|s - 1/2\| < \delta_0. The divergence transfer supplies a
single real point \sigma^* \in (1/2,\, 1/2 + \delta_0) with
B(\sigma^*) \;>\; \ln M_0 + C + c_T.
At s = \sigma^* all three pieces of G are real, with
\mathrm{Re}\, \tilde A_{f_\chi}(\sigma^*) \ge -C (real-segment bound, n_0 = 2) and
T(\sigma^*) \ge -c_T (uniform bound), so
\|L(\sigma^*, \chi)\| \;=\; e^{\mathrm{Re}\, G(\sigma^*)} \;\ge\; e^{\,B(\sigma^*) - C - c_T} \;>\; M_0,
contradicting the continuity bound at the very same point. No limit toward 1/2^{+} is
taken — the contradiction is evaluated at the one point \sigma^*.
\blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rintroThe `rintro` tactic is a combination of the `intros` tactic with `rcases` to
allow for destructuring patterns while introducing variables. See `rcases` for
a description of supported patterns. For example, `rintro (a | ⟨b, c⟩) ⟨d, e⟩`
will introduce two variables, and then do case splits on both of them producing
two subgoals, one with variables `a d e` and the other with `b c d e`.
`rintro`, unlike `rcases`, also supports the form `(x y : ty)` for introducing
and type-ascripting multiple variables at once, similar to binders.
⟨cℝ, Cℝ, hC∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ C⟩
-- Case `c ≠ 0` (CaseNonzero.lean) forces `c = 0`, leaving the bounded-race case.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hcc = 0 : cℝ = 0ℝ := c_eq_zero_of_raceSum_linearA362583.c_eq_zero_of_raceSum_linear {c C : ℝ} (hC : ∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ C) : c = 0**The slope is zero**: if the race sum is `c·π + O(1)`, then `c = 0`. This is what
reduces `raceSum_not_linear` to the bounded-race case `|raceSum N| ≤ C`.
If `c ≠ 0`, the Abel bound and the boundedness of `A` on `(1, 2]` give `|c|·P(σ) ≤ C + K` for
all `σ ∈ (1, 2]`, but divergence transfer (`exists_one_lt_tsum_primes_rpow_gt`, the sole prime
input `Σ 1/p = ∞`) produces a single `σ ∈ (1, 2)` where `P(σ) > (C + K)/|c|`. Contradiction. hC∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ C
subst`subst x...` substitutes each hypothesis `x` with a definition found in the local context,
then eliminates the hypothesis.
- If `x` is a local definition, then its definition is used.
- Otherwise, if there is a hypothesis of the form `x = e` or `e = x`,
then `e` is used for the definition of `x`.
If `h : a = b`, then `subst h` may be used if either `a` or `b` unfolds to a local hypothesis.
This is similar to the `cases h` tactic.
See also: `subst_vars` for substituting all local hypotheses that have a defining equation.
hcc = 0
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hS∀ (N : ℕ), |↑(raceSum N)| ≤ C : ∀ Nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
, |(raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of
`raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes
`≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`,
matching the convention of `Nat.primeCounting N` (# primes `≤ N`). Nℕ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. )| ≤ Cℝ := fun Nℕ ↦ by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. simpaThis is a "finishing" tactic modification of `simp`. It has two forms.
* `simpa [rules, ⋯] using e` will simplify the goal and the type of
`e` using `rules`, then try to close the goal using `e`.
Simplifying the type of `e` makes it more likely to match the goal
(which has also been simplified). This construction also tends to be
more robust under changes to the simp lemma set.
The final match between the simplified `e` and the simplified goal uses
**reducible** transparency, so it does not unfold semireducible definitions.
Write `simpa [rules, ⋯] using! e` to perform the match at the ambient
(default/semireducible) transparency instead.
* `simpa [rules, ⋯]` will simplify the goal and the type of a
hypothesis `this` if present in the context, then try to close the goal using
the `assumption` tactic.
As with `simp`, the `!` modifier after `simpa` enables auto-unfolding of
definitions in the simp set.
usingThe arguments to the `simpa` family tactics. hC∀ (N : ℕ), |↑(raceSum N) - 0 * ↑N.primeCounting| ≤ C Nℕ
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C : ∀ nℕ, ‖∑ kℕ ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`. (nℕ + 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at
primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`),
so `Complex.bpSeries fChi` is the by-parts continuation of `layerA`
(via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`). kℕ‖ ≤ Cℝ := norm_sum_range_fChi_leA362583.norm_sum_range_fChi_le {C : ℝ} (hS : ∀ (N : ℕ), |↑(raceSum N)| ≤ C) (n : ℕ) :
‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ CUnder the bounded-race hypothesis `|S(N)| ≤ C`, the partial sums of `fChi` are
bounded by `C` in norm — they are exactly the race sums (`sum_range_fChi`). hS∀ (N : ℕ), |↑(raceSum N)| ≤ C
-- Continuity of `L(χ, ·)` at `1/2`: a bound `M₀` on a `δ₀`-ball.
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨M₀ℝ, hM₀pos0 < M₀, δ₀ℝ, hδ₀pos0 < δ₀, hball∀ (s : ℂ), ‖s - ↑(1 / 2)‖ < δ₀ → ‖DirichletCharacter.LFunction χ s‖ < M₀⟩ := exists_norm_LFunction_lt_near_halfA362583.exists_norm_LFunction_lt_near_half :
∃ M₀, 0 < M₀ ∧ ∃ δ₀, 0 < δ₀ ∧ ∀ (s : ℂ), ‖s - ↑(1 / 2)‖ < δ₀ → ‖DirichletCharacter.LFunction χ s‖ < M₀`L(χ, ·)` is entire, hence continuous at `1/2`: there are `M₀ > 0` and `δ₀ > 0` with
`‖L(χ, s)‖ < M₀` for every `s` within `δ₀` of `1/2`. No bounded-race hypothesis is used.
-- Divergence transfer: a single point `σ ∈ (1/2, 1/2 + δ₀)` where `layerBReal` is huge.
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨σℝ, hσlo1 / 2 < σ, hσhiσ < 1 / 2 + δ₀, hσBReal.log M₀ + C + cT < layerBReal σ⟩ := exists_layerBReal_gtA362583.exists_layerBReal_gt (M : ℝ) {η : ℝ} (hη : 0 < η) : ∃ σ, 1 / 2 < σ ∧ σ < 1 / 2 + η ∧ M < layerBReal σ**Divergence transfer at `s ↓ 1/2` (`layerBReal` blow-up)**: for every `M` and
`η > 0` there is a real `σ ∈ (1/2, 1/2+η)` with `layerBReal σ > M`. Only prime
input: the divergence of `Σ 1/p`. (Real.logReal.log (x : ℝ) : ℝThe real logarithm function, equal to the inverse of the exponential for `x > 0`,
to `log |x|` for `x < 0`, and to `0` for `0`. We use this unconventional extension to
`(-∞, 0]` as it gives the formula `log (x * y) = log x + log y` for all nonzero `x` and `y`, and
the derivative of `log` is `1/x` away from `0`. M₀ℝ + Cℝ + cTA362583.cT : ℝExplicit uniform bound for `layerT` on `Re s ≥ 1/2`:
`cT = (4/3) Σ_p p^(-3/2)` (a crude `κ ≤ 4` version of the `κ/3 · Σ n^(-3/2)` bound). ) hδ₀pos0 < δ₀
-- At `σ` the identity gives `‖L(χ, σ)‖ = exp (Re (contLog σ)) > M₀` …
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hGgtReal.log M₀ < (contLog ↑σ).re : Real.logReal.log (x : ℝ) : ℝThe real logarithm function, equal to the inverse of the exponential for `x > 0`,
to `log |x|` for `x < 0`, and to `0` for `0`. We use this unconventional extension to
`(-∞, 0]` as it gives the formula `log (x * y) = log x + log y` for all nonzero `x` and `y`, and
the derivative of `log` is `1/x` away from `0`. M₀ℝ < (contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi`
is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3`
layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}`
(`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`). ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )).reComplex.re (self : ℂ) : ℝThe real part of a complex number. := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
:= layerBReal_sub_le_re_contLogA362583.layerBReal_sub_le_re_contLog {C : ℝ} (hB : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C) {σ : ℝ}
(hσ : 1 / 2 < σ) : layerBReal σ - C - cT ≤ (contLog ↑σ).reUnder the bounded-race hypothesis, `Re (contLog σ)` is bounded below by
`layerBReal σ - C - cT` on `Ω`: writing
`Re (contLog σ) = Re (bpSeries fChi σ) + layerBReal σ + layerTReal σ`, the by-parts real
part is `≥ -C` (`norm_bpSeries_le_const`) and `layerTReal σ ≥ -cT` (`abs_layerTReal_le`). hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C hσlo1 / 2 < σ
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hLbigM₀ < ‖DirichletCharacter.LFunction χ ↑σ‖ : M₀ℝ < ‖DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )‖ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[norm_LFunction_eq_exp_re_contLogA362583.norm_LFunction_eq_exp_re_contLog {C : ℝ} (hB : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C) {σ : ℝ}
(hσ : 1 / 2 < σ) : ‖DirichletCharacter.LFunction χ ↑σ‖ = Real.exp (contLog ↑σ).reUnder the bounded-race hypothesis, on `Ω` the L-function has norm `exp (Re (contLog σ))`:
for real `σ` with `1/2 < σ`, `‖L(χ, σ)‖ = Real.exp ((contLog σ).re)`, from the identity
`exp ∘ contLog = L(χ, ·)` (`exp_contLog_eqOn`). hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C hσlo1 / 2 < σ]
calcStep-wise reasoning over transitive relations.
```
calc
a = b := pab
b = c := pbc
...
y = z := pyz
```
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
relation implementing the typeclass `Trans`. Instead of repeating the right-
hand sides, subsequent left-hand sides can be replaced with `_`.
```
calc
a = b := pab
_ = c := pbc
...
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc
_ = bce := pabce
_ = cef := pbcef
...
_ = xyz := pwxyz
```
`calc` works as a term, as a tactic or as a `conv` tactic.
See [Theorem Proving in Lean 4][tpil4] for more information.
[tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
M₀ℝ = Real.expReal.exp (x : ℝ) : ℝThe real exponential function, defined as the real part of the complex exponential (Real.logReal.log (x : ℝ) : ℝThe real logarithm function, equal to the inverse of the exponential for `x > 0`,
to `log |x|` for `x < 0`, and to `0` for `0`. We use this unconventional extension to
`(-∞, 0]` as it gives the formula `log (x * y) = log x + log y` for all nonzero `x` and `y`, and
the derivative of `log` is `1/x` away from `0`. M₀ℝ) := (Real.exp_logReal.exp_log {x : ℝ} (hx : 0 < x) : Real.exp (Real.log x) = x hM₀pos0 < M₀).symmEq.symm.{u} {α : Sort u} {a b : α} (h : a = b) : b = aEquality is symmetric: if `a = b` then `b = a`.
Because this is in the `Eq` namespace, if you have a variable `h : a = b`,
`h.symm` can be used as shorthand for `Eq.symm h` as a proof of `b = a`.
For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality)
_ < Real.expReal.exp (x : ℝ) : ℝThe real exponential function, defined as the real part of the complex exponential ((contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi`
is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3`
layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}`
(`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`). ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )).reComplex.re (self : ℂ) : ℝThe real part of a complex number. ) := Real.exp_lt_expReal.exp_lt_exp {x y : ℝ} : Real.exp x < Real.exp y ↔ x < y.mprIff.mpr {a b : Prop} (self : a ↔ b) : b → aModus ponens for if and only if, reversed. If `a ↔ b` and `b`, then `a`. hGgtReal.log M₀ < (contLog ↑σ).re
-- … but `σ` is within `δ₀` of `1/2`, so `‖L(χ, σ)‖ < M₀`. Contradiction.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hLsmall‖DirichletCharacter.LFunction χ ↑σ‖ < M₀ : ‖DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((σℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )‖ < M₀ℝ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
hball∀ (s : ℂ), ‖s - ↑(1 / 2)‖ < δ₀ → ‖DirichletCharacter.LFunction χ s‖ < M₀ _ ?_
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[← Complex.ofReal_subComplex.ofReal_sub (r s : ℝ) : ↑(r - s) = ↑r - ↑s, Complex.norm_realComplex.norm_real (r : ℝ) : ‖↑r‖ = ‖r‖, Real.norm_eq_absReal.norm_eq_abs (r : ℝ) : ‖r‖ = |r|,
abs_of_posabs_of_pos.{u_1} {α : Type u_1} [Lattice α] [AddGroup α] {a : α} [AddLeftMono α] (h : 0 < a) : |a| = a (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
)]
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
The k \ge 3 layer T(s) = \sum_p t_p(s), summed over all primes. The per-prime bound
|t_p| \le \tfrac43 p^{-3/2} feeds the Weierstrass M-test, so T is holomorphic on
\Omega with the uniform bound \|T(s)\| \le c_T := \tfrac43 \sum_p p^{-3/2} on
\mathrm{Re}\, s \ge 1/2, and real on the real axis.
Lean code for Definition layerT
-
⟲
def layerT (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.def layerT (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.
⟲:= ∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , tpA362583.tp (p : Nat.Primes) (s : ℂ) : ℂPer-prime `k ≥ 3` tail `t_p(s) = Σ_{k≥0} (χ(p) p^(-s))^(k+3) / (k+3)`.
The compact form `z_p^(k+3)/(k+3)` (rather than the expanded
`χ(p)^(k+3) p^(-(k+3)s)/(k+3)`) is chosen so that the tail produced by peeling three
terms off `hasSum_neg_log_one_sub` is *definitionally* `tp p s` (see `neg_log_split`). pNat.Primes sℂ
Real companion of the k = 2 layer. On the real axis B is repackaged with real powers
as B(\sigma), agreeing with the complex B at real arguments. This is the form the
c = 0 case evaluates: B(\sigma) \ge 0, and B(\sigma) \le c_B := \tfrac12 \sum_p p^{-2}
for \sigma \ge 1. The blow-up of B(\sigma) as \sigma \downarrow 1/2 is not proved
here — it is one instance of the divergence transfer below.
Lean code for Definition layerBReal
-
⟲
def layerBReal (σ
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.def layerBReal (σ
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.
⟲:= (1ℝ / 2ℝ) * ∑' pNat.Primes : Nat.PrimesNat.Primes : TypeThe type of prime numbers , if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
(pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) = 2ℕ then`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
0ℝ else`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) ^ (-(2ℝ * σℝ))
Divergence transfer to layer B. For every M and \eta > 0 there is
\sigma \in (1/2, 1/2+\eta) with B(\sigma) > M — the blow-up of the k = 2 layer just
above 1/2 that drives the c = 0 case.
Lean code for Lemma exists_layerBReal_gt
-
≈
theorem exists_layerBReal_gt (M
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) {ηℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hη0 < η: 0ℝ< ηℝ) : ∃ σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., 1ℝ/ 2ℝ< σℝ∧ σℝ< 1ℝ/ 2ℝ+ ηℝ∧ Mℝ< layerBRealA362583.layerBReal (σ : ℝ) : ℝReal companion of `layerB` (real `rpow`; agreement: `layerB_ofReal`).σℝtheorem exists_layerBReal_gt (M
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) {ηℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hη0 < η: 0ℝ< ηℝ) : ∃ σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., 1ℝ/ 2ℝ< σℝ∧ σℝ< 1ℝ/ 2ℝ+ ηℝ∧ Mℝ< layerBRealA362583.layerBReal (σ : ℝ) : ℝReal companion of `layerB` (real `rpow`; agreement: `layerB_ofReal`).σℝ
Recall B(\sigma) = \tfrac12 \sum_{p \text{ odd}} p^{-2\sigma}. The divergence of \sum_p 1/p
survives dropping the p = 2 term, so the finite subsums over odd primes are unbounded:
choose a finite set F of odd primes with \sum_{p \in F} 1/p > 2M. At \sigma = 1/2,
\sum_{p \in F} p^{-2\sigma} = \sum_{p \in F} p^{-1} > 2M, and by continuity in \sigma this
persists at some \sigma \in (1/2, 1/2+\eta). There the odd-prime series dominates its finite
subsum over F (nonnegative terms), so
B(\sigma) \ge \tfrac12 \sum_{p \in F} p^{-2\sigma} > \tfrac12 \cdot 2M = M. As in the
prime-sum instance, a single point \sigma^* suffices. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨F, hFodd, hF⟩ := exists_odd_finset_one_div_gt (2 * M)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hF1 : 2 * M < ∑ p ∈ F, ((p : ℕ) : ℝ) ^ (-(2 * (1 / 2 : ℝ))) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
hF.trans_le (le_of_eq (Finset.sum_congr rfl fun p _ ↦ ?_))
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[show -(2 * (1 / 2 : ℝ)) = -1 by norm_num`norm_num` normalizes numerical expressions in the goal. By default, it supports the operations
`+` `-` `*` `/` `⁻¹` `^` and `%` over types with (at least) an `AddMonoidWithOne` instance, such as
`ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ`. In addition to evaluating numerical expressions, `norm_num` will use `simp`
to simplify the goal. If the goal has the form `A = B`, `A ≠ B`, `A < B` or `A ≤ B`, where `A` and
`B` are numerical expressions, `norm_num` will try to close it. It also has a relatively simple
primality prover (available if you import `Mathlib.Tactic.NormNum.Prime`).
This tactic is extensible. Extensions can allow `norm_num` to evaluate more kinds of expressions, or
to prove more kinds of propositions (such as, primality of natural numbers). See the `@[norm_num]`
attribute for further information on extending `norm_num`.
* `norm_num at l` normalizes at location(s) `l`.
* `norm_num [h1, ...]` adds the arguments `h1, ...` to the `simp` set in addition to the default
`simp` set. All options for `simp` arguments are supported, in particular `←`, `↑` and `↓`.
* `norm_num only` does not use the default `simp` set for simplification. `norm_num only [h1, ...]`
uses only the arguments `h1, ...` in addition to the routines tagged `@[norm_num]`.
`norm_num only` still performs post-processing steps, like `simp only`, use `norm_num1` if you
exclusively want to normalize numerical expressions.
* `norm_num (config := cfg)` uses `cfg` as configuration for `simp` calls (see the `simp` tactic for
further details).
Examples:
```lean
example : 43 ≤ 74 + (33 : ℤ) := by norm_num
example : ¬ (7-2)/(2*3) ≥ (1:ℝ) + 2/(3^2) := by norm_num
```
, Real.rpow_neg_one, one_div]
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨u, hu1, hu2, hu3⟩ := exists_right_of_sum_rpow_gt hF1 hη
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hsum := summable_layerBReal_term (σ := u) hu1
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hle : ∑ p ∈ F, ((p : ℕ) : ℝ) ^ (-(2 * u))
≤ ∑' p : Nat.Primes, if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
(p : ℕ) = 2 then`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
0 else`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
((p : ℕ) : ℝ) ^ (-(2 * u)) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
heq : ∑ p ∈ F, ((p : ℕ) : ℝ) ^ (-(2 * u))
= ∑ p ∈ F, if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
(p : ℕ) = 2 then`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
0 else`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
((p : ℕ) : ℝ) ^ (-(2 * u)) :=
Finset.sum_congr rfl fun p hp ↦ (if_neg (hFodd p hp)).symm
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[heq]
exact`exact e` closes the main goal if its target type matches that of `e`.
hsum.sum_le_tsum F fun p _ ↦ by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rcases`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
eq_or_ne (p : ℕ) 2 with`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
h | h
· simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[h]
· simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[if_neg h]
positivity`positivity` solves goals of the form `0 ≤ x`, `0 < x` and `x ≠ 0`. The tactic works recursively
according to the syntax of the expression `x`, by attempting to prove subexpressions are
positive/nonnegative/nonzero and combining this into a final proof. This tactic either closes the
goal or fails.
For each subexpression `e`, `positivity` will try to:
* try `@[positivity]`-tagged extensions to recursively prove `e` is positive/nonnegative/nonzero
based on its subexpressions (see the `positivity` attribute for more details), or
* try the `norm_num` tactic to prove `e` is positive/nonnegative/nonzero, or
* try showing `e : t` is nonnegative because there is a `CanonicallyOrderedAdd t` instance, or
* use a local hypothesis of the form `0 ≤ e`, `0 < e` or `e ≠ 0`.
This tactic is extensible. See the `positivity` attribute documentation for more details.
* `positivity [t₁, …, tₙ]` first executes `have := t₁; …; have := tₙ` in the current goal,
then runs `positivity`. This is useful when `positivity` needs derived premises such as `0 < y`
for division/reciprocal, or `0 ≤ x` for real powers.
Examples:
```
example {a : ℤ} (ha : 3 < a) : 0 ≤ a ^ 3 + a := by positivity
example {a : ℤ} (ha : 1 < a) : 0 < |(3:ℤ) + a| := by positivity
example {b : ℤ} : 0 ≤ max (-3) (b ^ 2) := by positivity
example {a b c d : ℝ} (hab : 0 < a * b) (hb : 0 ≤ b) (hcd : c < d) :
0 < a ^ c + 1 / (d - c) := by
positivity [sub_pos_of_lt hcd, pos_of_mul_pos_left hab hb]
```
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
⟨u, hu1, hu2, ?_⟩
unfold* `unfold id` unfolds all occurrences of definition `id` in the target.
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
* `unfold id at h` unfolds at the hypothesis `h`.
Definitions can be either global or local definitions.
For non-recursive global definitions, this tactic is identical to `delta`.
For recursive global definitions, it uses the "unfolding lemma" `id.eq_def`,
which is generated for each recursive definition, to unfold according to the recursive definition given by the user.
Only one level of unfolding is performed, in contrast to `simp only [id]`, which unfolds definition `id` recursively.
layerBReal
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
Forcing c = 0. If
|S(N) - c\,\pi(N)| \le C for all N, then c = 0.
Lean code for Theorem c_eq_zero_of_raceSum_linear
-
⟲
theorem c_eq_zero_of_raceSum_linear {c
ℝCℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ C: ∀ Nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., |(raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of `raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes `≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`, matching the convention of `Nat.primeCounting N` (# primes `≤ N`).Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) - cℝ* (Nat.primeCountingNat.primeCounting (n : ℕ) : ℕThe prime counting function: Returns the number of primes less than or equal to the input. With `open scoped Nat.Prime`, this has notation `π`.Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.)| ≤ Cℝ) : cℝ= 0ℝtheorem c_eq_zero_of_raceSum_linear {c
ℝCℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ C: ∀ Nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., |(raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of `raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes `≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`, matching the convention of `Nat.primeCounting N` (# primes `≤ N`).Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) - cℝ* (Nat.primeCountingNat.primeCounting (n : ℕ) : ℕThe prime counting function: Returns the number of primes less than or equal to the input. With `open scoped Nat.Prime`, this has notation `π`.Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.)| ≤ Cℝ) : cℝ= 0ℝ
Work with real \sigma \in (1, 2] and write P(\sigma) = \sum_p p^{-\sigma}.
Step 1 (Abel bound). Apply the by-parts machinery to the shifted coefficients
f_c(n) := f_\chi(n) - c \cdot \mathbf{1}_{\mathrm{prime}}(n): their partial sums are
exactly the race deviation S(n) - c\,\pi(n), bounded by C by hypothesis and vanishing
below n = 2. The real-segment bound plus the Dirichlet-series identification give
\bigl| A(\sigma) - c\, P(\sigma) \bigr| \;\le\; C \qquad \text{on } (1, 2].
Step 2 (logarithm bound). For real \sigma > 1 the three layers are real, so the Euler
identity reads L(\sigma, \chi) = e^{A(\sigma) + B(\sigma) + T(\sigma)} — a positive
real — and injectivity of the real exponential inverts it to
\ln L(\sigma) = A(\sigma) + B(\sigma) + T(\sigma); no complex logarithm is ever inverted.
At the endpoint, L(1, \chi) > 0: it is the limit of the positive values
L(\sigma) as \sigma \downarrow 1 (the one plain one-sided limit surviving in the whole
development), and it is nonzero by Mathlib's nonvanishing at 1. Hence
\mathrm{Re}\, L(\cdot, \chi) is continuous and positive on the compact [1, 2], its
logarithm is bounded there by some K_0, and with the layer bounds
0 \le B \le c_B, |T| \le c_T this yields
|A(\sigma)| \le K := K_0 + c_B + c_T on (1, 2].
Step 3 (single-point contradiction). If c \ne 0, steps 1–2 force
P(\sigma) \le (C + K)/|c| throughout (1, 2]. But the prime-sum divergence supplies a
single \sigma^* \in (1, 2) with P(\sigma^*) > (C + K)/|c|. Contradiction; hence
c = 0. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
by_contra`by_contra h` proves `⊢ p` by contradiction,
introducing a hypothesis `h : ¬p` and proving `False`.
* If `p` is a negation `¬q`, `h : q` will be introduced instead of `¬¬q`.
* If `p` is decidable, it uses `Decidable.byContradiction` instead of `Classical.byContradiction`.
* If `h` is omitted, the introduced variable will be called `this`.
* `h` can be any pattern supported by `rcases`/`rintro`.
hc¬c = 0
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨Kℝ, hK∀ (σ : ℝ), 1 < σ → σ ≤ 2 → |layerAReal σ| ≤ K⟩ := exists_bound_abs_layerARealA362583.exists_bound_abs_layerAReal : ∃ K, ∀ (σ : ℝ), 1 < σ → σ ≤ 2 → |layerAReal σ| ≤ K**`A` bounded on `(1, 2]`**: a uniform bound `|A(σ)| ≤ K`, from continuity and
positivity of `Re L(χ, ·)` on the compact `[1, 2]` plus the `B`/`T` bounds.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hcpos0 < |c| : 0ℝ < |cℝ| := abs_posabs_pos.{u_1} {α : Type u_1} [AddGroup α] [LinearOrder α] [AddLeftMono α] {a : α} : 0 < |a| ↔ a ≠ 0.mprIff.mpr {a b : Prop} (self : a ↔ b) : b → aModus ponens for if and only if, reversed. If `a ↔ b` and `b`, then `a`. hc¬c = 0
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨σℝ, hσ11 < σ, hσ2σ < 1 + 1, hσP(C + K) / |c| < ∑' (p : Nat.Primes), ↑↑p ^ (-σ)⟩ :=
exists_one_lt_tsum_primes_rpow_gtA362583.exists_one_lt_tsum_primes_rpow_gt (M : ℝ) {η : ℝ} (hη : 0 < η) :
∃ σ, 1 < σ ∧ σ < 1 + η ∧ M < ∑' (p : Nat.Primes), ↑↑p ^ (-σ)**Divergence transfer at `s ↓ 1`**: for every `M` and `η > 0` there is a
real `σ ∈ (1, 1+η)` with `P(σ) = Σ_p p^(-σ) > M`. Only prime input: the
divergence of `Σ 1/p`. ((Cℝ + Kℝ) / |cℝ|) (η := 1ℝ) one_posone_pos.{u_1} {α : Type u_1} [Zero α] [One α] [PartialOrder α] [ZeroLEOneClass α] [NeZero 1] : 0 < 1**Alias** of `zero_lt_one`.
---
See `zero_lt_one'` for a version with the type explicit.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hσP'(C + K) / |c| < primeSum σ : (Cℝ + Kℝ) / |cℝ| < primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ := hσP(C + K) / |c| < ∑' (p : Nat.Primes), ↑↑p ^ (-σ)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hstep1|layerAReal σ - c * primeSum σ| ≤ C := abs_layerAReal_sub_mul_primeSum_leA362583.abs_layerAReal_sub_mul_primeSum_le {c C : ℝ} (hC : ∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ C) {σ : ℝ}
(hσ : 1 < σ) : |layerAReal σ - c * primeSum σ| ≤ C**Abel bound** (real form): under the linearity hypothesis,
`|A(σ) - c·P(σ)| ≤ C` for real `σ > 1`. hC∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ C hσ11 < σ
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hstep2|layerAReal σ| ≤ K := hK∀ (σ : ℝ), 1 < σ → σ ≤ 2 → |layerAReal σ| ≤ K σℝ hσ11 < σ (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
)
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[abs_leabs_le.{u_1} {G : Type u_1} [AddCommGroup G] [LinearOrder G] [IsOrderedAddMonoid G] {a b : G} : |a| ≤ b ↔ -b ≤ a ∧ a ≤ b] atLocation specifications are used by many tactics that can operate on either the
hypotheses or the goal. It can have one of the forms:
* 'empty' is not actually present in this syntax, but most tactics use
`(location)?` matchers. It means to target the goal only.
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
* `at *`: target all hypotheses and the goal
hstep1|layerAReal σ - c * primeSum σ| ≤ C hstep2|layerAReal σ| ≤ K
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hPnonneg0 ≤ primeSum σ : 0ℝ ≤ primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ :=
tsum_nonnegtsum_nonneg.{u_1, u_3} {ι : Type u_1} {α : Type u_3} {L : SummationFilter ι} [AddCommMonoid α] [Preorder α]
[IsOrderedAddMonoid α] [TopologicalSpace α] [OrderClosedTopology α] {g : ι → α} (h : ∀ (i : ι), 0 ≤ g i) :
0 ≤ ∑'[L] (i : ι), g i fun pNat.Primes ↦ Real.rpow_nonnegReal.rpow_nonneg {x : ℝ} (hx : 0 ≤ x) (y : ℝ) : 0 ≤ x ^ y (Nat.cast_nonnegNat.cast_nonneg.{u_3} {α : Type u_3} [Semiring α] [PartialOrder α] [IsOrderedRing α] (n : ℕ) : 0 ≤ ↑nSpecialisation of `Nat.cast_nonneg'`, which seems to be easier for Lean to use. _) _
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hgtC + K < |c| * primeSum σ : Cℝ + Kℝ < |cℝ| * primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hC + K < primeSum σ * |c| := (div_lt_iff₀div_lt_iff₀.{u_3} {G₀ : Type u_3} [GroupWithZero G₀] [PartialOrder G₀] [MulPosReflectLT G₀] {a b c : G₀} (hc : 0 < c) :
b / c < a ↔ b < a * cSee `div_lt_iff₀'` for a version with multiplication on the other side. hcpos0 < |c|).mpIff.mp {a b : Prop} (self : a ↔ b) : a → bModus ponens for if and only if. If `a ↔ b` and `a`, then `b`. hσP'(C + K) / |c| < primeSum σ
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
[mul_commmul_comm.{u_1} {G : Type u_1} [CommMagma G] (a b : G) : a * b = b * a (primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ) (|cℝ|)]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hle|c| * primeSum σ ≤ C + K : |cℝ| * primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ ≤ Cℝ + Kℝ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
habs|c * primeSum σ| ≤ C + K : |cℝ * primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ| ≤ Cℝ + Kℝ := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hrewc * primeSum σ = layerAReal σ - (layerAReal σ - c * primeSum σ) : cℝ * primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ = layerARealA362583.layerAReal (σ : ℝ) : ℝReal companion of `layerA` (real `rpow`; agreement: `layerA_ofReal`). σℝ - (layerARealA362583.layerAReal (σ : ℝ) : ℝReal companion of `layerA` (real `rpow`; agreement: `layerA_ofReal`). σℝ - cℝ * primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. ring`ring` solves equations in *commutative* (semi)rings, allowing for variables in the
exponent. If the goal is not appropriate for `ring` (e.g. not an equality) `ring_nf` will be
suggested. See also `ring1`, which fails if the goal is not an equality.
* `ring!` will use a more aggressive reducibility setting to determine equality of atoms.
Examples:
```
example (n : ℕ) (m : ℤ) : 2^(n+1) * m = 2 * 2^n * m := by ring
example (a b : ℤ) (n : ℕ) : (a + b)^(n + 2) = (a^2 + b^2 + a * b + b * a) * (a + b)^n := by ring
example (x y : ℕ) : x + id y = y + id x := by ring!
example (x : ℕ) (h : x * 2 > 5): x + x > 5 := by ring; assumption -- suggests ring_nf
```
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hrewc * primeSum σ = layerAReal σ - (layerAReal σ - c * primeSum σ), abs_leabs_le.{u_1} {G : Type u_1} [AddCommGroup G] [LinearOrder G] [IsOrderedAddMonoid G] {a b : G} : |a| ≤ b ↔ -b ≤ a ∧ a ≤ b]
exact`exact e` closes the main goal if its target type matches that of `e`.
⟨And.intro {a b : Prop} (left : a) (right : b) : a ∧ b`And.intro : a → b → a ∧ b` is the constructor for the And operation. by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
[hstep1-C ≤ layerAReal σ - c * primeSum σ ∧ layerAReal σ - c * primeSum σ ≤ C.2And.right {a b : Prop} (self : a ∧ b) : bExtract the right conjunct from a conjunction. `h : a ∧ b` then
`h.right`, also notated as `h.2`, is a proof of `b`. , hstep2-K ≤ layerAReal σ ∧ layerAReal σ ≤ K.1And.left {a b : Prop} (self : a ∧ b) : aExtract the left conjunct from a conjunction. `h : a ∧ b` then
`h.left`, also notated as `h.1`, is a proof of `a`. ],And.intro {a b : Prop} (left : a) (right : b) : a ∧ b`And.intro : a → b → a ∧ b` is the constructor for the And operation. by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
[hstep1-C ≤ layerAReal σ - c * primeSum σ ∧ layerAReal σ - c * primeSum σ ≤ C.1And.left {a b : Prop} (self : a ∧ b) : aExtract the left conjunct from a conjunction. `h : a ∧ b` then
`h.left`, also notated as `h.1`, is a proof of `a`. , hstep2-K ≤ layerAReal σ ∧ layerAReal σ ≤ K.2And.right {a b : Prop} (self : a ∧ b) : bExtract the right conjunct from a conjunction. `h : a ∧ b` then
`h.right`, also notated as `h.2`, is a proof of `b`. ]⟩And.intro {a b : Prop} (left : a) (right : b) : a ∧ b`And.intro : a → b → a ∧ b` is the constructor for the And operation.
calcStep-wise reasoning over transitive relations.
```
calc
a = b := pab
b = c := pbc
...
y = z := pyz
```
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
relation implementing the typeclass `Trans`. Instead of repeating the right-
hand sides, subsequent left-hand sides can be replaced with `_`.
```
calc
a = b := pab
_ = c := pbc
...
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc
_ = bce := pabce
_ = cef := pbcef
...
_ = xyz := pwxyz
```
`calc` works as a term, as a tactic or as a `conv` tactic.
See [Theorem Proving in Lean 4][tpil4] for more information.
[tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
|cℝ| * primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ = |cℝ| * |primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ| := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[abs_of_nonnegabs_of_nonneg.{u_1} {α : Type u_1} [Lattice α] [AddGroup α] {a : α} [AddLeftMono α] (h : 0 ≤ a) : |a| = a hPnonneg0 ≤ primeSum σ]
_ = |cℝ * primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ| := (abs_mulabs_mul.{u_1} {α : Type u_1} [Ring α] [LinearOrder α] [IsOrderedRing α] (a b : α) : |a * b| = |a| * |b| cℝ (primeSumA362583.primeSum (σ : ℝ) : ℝThe real prime series `P(σ) = Σ_p p^(-σ)`. σℝ)).symmEq.symm.{u} {α : Sort u} {a b : α} (h : a = b) : b = aEquality is symmetric: if `a = b` then `b = a`.
Because this is in the `Eq` namespace, if you have a variable `h : a = b`,
`h.symm` can be used as shorthand for `Eq.symm h` as a proof of `b = a`.
For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality)
_ ≤ Cℝ + Kℝ := habs|c * primeSum σ| ≤ C + K
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
The per-prime k \ge 3 tail. With z_p := \chi(p)\, p^{-s},
t_p(s) \;=\; \sum_{k \ge 0} \frac{z_p^{\,k+3}}{k+3}
— the terms of the logarithm series from the third onward. Geometric tails give the
per-prime bound |t_p(s)| \le \tfrac43 p^{-3/2} on \mathrm{Re}\, s \ge 1/2. (The constant
is deliberately loose; nothing downstream needs a tight bound.)
Lean code for Definition tp
-
⟲
def tp (p
Nat.Primes: Nat.PrimesNat.Primes : TypeThe type of prime numbers) (sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.def tp (p
Nat.Primes: Nat.PrimesNat.Primes : TypeThe type of prime numbers) (sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.
⟲:= ∑' kℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
, (χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)) ^ (kℕ + 3ℕ) / ((kℕ + 3ℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )
Real-segment bound, constant form. Under the same hypotheses, for real \sigma \ge 0,
\bigl\| \tilde A_f(\sigma) \bigr\| \;\le\; C.
This is the convenience form consumed downstream, where only a uniform bound on the segment is
needed.
Lean code for Lemma norm_bpSeries_le_const
-
⟲
theorem norm_bpSeries_le_const (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) {n₀ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn₀1 ≤ n₀: 1ℕ≤ n₀ℕ) (hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0: ∀ nℕ< n₀ℕ, ∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ= 0ℂ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ0 ≤ σ: 0ℝ≤ σℝ) : ‖bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂσℝ‖ ≤ Cℝtheorem norm_bpSeries_le_const (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) {n₀ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn₀1 ≤ n₀: 1ℕ≤ n₀ℕ) (hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0: ∀ nℕ< n₀ℕ, ∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ= 0ℂ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ0 ≤ σ: 0ℝ≤ σℝ) : ‖bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂσℝ‖ ≤ Cℝ
Immediate from the real-segment bound: n_0^{-\sigma} \le 1 because n_0 \ge 1 and
-\sigma \le 0, so C\, n_0^{-\sigma} \le C. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hC00 ≤ C : 0ℝ ≤ Cℝ := (norm_nonnegnorm_nonneg.{u_5} {E : Type u_5} [SeminormedAddGroup E] (a : E) : 0 ≤ ‖a‖ _).transLE.le.trans.{u_1} {α : Type u_1} [Preorder α] {a b c : α} : a ≤ b → b ≤ c → a ≤ c**Alias** of `le_trans`. (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C 0ℕ)
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
(norm_bpSeries_lenorm_bpSeries_le {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {n₀ : ℕ} (hn₀ : 1 ≤ n₀)
(hvanish : ∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0) {σ : ℝ} (hσ : 0 ≤ σ) : ‖bpSeries f ↑σ‖ ≤ C * ↑n₀ ^ (-σ)**Real-segment bound** (real-axis telescoping): if the partial sums of `f`
are bounded by `C` and vanish for `n < n₀` (with `1 ≤ n₀`), then for real `σ ≥ 0`
`‖bpSeries f σ‖ ≤ C * (n₀ : ℝ) ^ (-σ)`.
The by-parts series converges absolutely (`sum_range_norm_bpSeries_le` bounds its partial sums)
and its norm is at most the telescoping majorant `C · n₀ ^ (-σ)`. (For the race-sum
application: partial sums over `k ≤ 0` and `k ≤ 1` vanish since there are no primes below `2`,
so `n₀ = 2` and the bound is `C * 2 ^ (-σ)`.) hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C hn₀1 ≤ n₀ hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0 hσ0 ≤ σ).transLE.le.trans.{u_1} {α : Type u_1} [Preorder α] {a b c : α} : a ≤ b → b ≤ c → a ≤ c**Alias** of `le_trans`. ?_
calcStep-wise reasoning over transitive relations.
```
calc
a = b := pab
b = c := pbc
...
y = z := pyz
```
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
relation implementing the typeclass `Trans`. Instead of repeating the right-
hand sides, subsequent left-hand sides can be replaced with `_`.
```
calc
a = b := pab
_ = c := pbc
...
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc
_ = bce := pabce
_ = cef := pbcef
...
_ = xyz := pwxyz
```
`calc` works as a term, as a tactic or as a `conv` tactic.
See [Theorem Proving in Lean 4][tpil4] for more information.
[tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
Cℝ * (n₀ℕ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) ^ (-σℝ) ≤ Cℝ * 1ℝ :=
mul_le_mul_of_nonneg_leftmul_le_mul_of_nonneg_left.{u_1} {α : Type u_1} [Mul α] [Zero α] [Preorder α] {a b c : α} [PosMulMono α] (hbc : b ≤ c)
(ha : 0 ≤ a) : a * b ≤ a * c
(Real.rpow_le_one_of_one_le_of_nonposReal.rpow_le_one_of_one_le_of_nonpos {x z : ℝ} (hx : 1 ≤ x) (hz : z ≤ 0) : x ^ z ≤ 1 (Nat.one_le_castNat.one_le_cast.{u_1} {α : Type u_1} [AddMonoidWithOne α] [PartialOrder α] [AddLeftMono α] [ZeroLEOneClass α]
[CharZero α] {n : ℕ} : 1 ≤ ↑n ↔ 1 ≤ n.2Iff.mpr {a b : Prop} (self : a ↔ b) : b → aModus ponens for if and only if, reversed. If `a ↔ b` and `b`, then `a`. hn₀1 ≤ n₀) (neg_nonposneg_nonpos.{u} {α : Type u} [AddGroup α] [LE α] [AddLeftMono α] {a : α} : -a ≤ 0 ↔ 0 ≤ a**Alias** of `Left.neg_nonpos_iff`.
---
Uses `left` co(ntra)variant. .2Iff.mpr {a b : Prop} (self : a ↔ b) : b → aModus ponens for if and only if, reversed. If `a ↔ b` and `b`, then `a`. hσ0 ≤ σ))
hC00 ≤ C
_ = Cℝ := mul_onemul_one.{u} {M : Type u} [MulOneClass M] (a : M) : a * 1 = a Cℝ
Identity theorem. Under the bounded-race hypothesis,
\exp\bigl(G(s)\bigr) \;=\; L(s, \chi) \qquad \text{for all } s \in \Omega = \{\mathrm{Re}\, s > 1/2\}.
Lean code for Theorem exp_contLog_eqOn
-
≈
theorem exp_contLog_eqOn {C
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).kℕ‖ ≤ Cℝ) : Set.EqOnSet.EqOn.{u, v} {α : Type u} {β : Type v} (f₁ f₂ : α → β) (s : Set α) : PropTwo functions `f₁ f₂ : α → β` are equal on `s` if `f₁ x = f₂ x` for all `x ∈ s`.(fun sℂ↦ Complex.expComplex.exp (z : ℂ) : ℂThe complex exponential function, defined via its Taylor series(contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi` is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3` layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}` (`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`).sℂ)) (DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions. Note that this is not the same as `LSeries χ`: they agree in the convergence range, but `LSeries χ s` is defined to be `0` if `re s ≤ 1`.χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.| 1ℝ/ 2ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.}theorem exp_contLog_eqOn {C
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hB∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), fChi k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`), so `Complex.bpSeries fChi` is the by-parts continuation of `layerA` (via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`).kℕ‖ ≤ Cℝ) : Set.EqOnSet.EqOn.{u, v} {α : Type u} {β : Type v} (f₁ f₂ : α → β) (s : Set α) : PropTwo functions `f₁ f₂ : α → β` are equal on `s` if `f₁ x = f₂ x` for all `x ∈ s`.(fun sℂ↦ Complex.expComplex.exp (z : ℂ) : ℂThe complex exponential function, defined via its Taylor series(contLogA362583.contLog (s : ℂ) : ℂThe continued logarithm `G := Ã + B + T`, where `Ã = bpSeries fChi` is the by-parts continuation of the `k = 1` layer and `B`, `T` are the `k = 2` and `k ≥ 3` layers. Under the bounded-race hypothesis it is holomorphic on `Ω = {Re s > 1/2}` (`differentiableOn_contLog`) and `exp ∘ contLog = L(χ, ·)` there (`exp_contLog_eqOn`).sℂ)) (DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions. Note that this is not the same as `LSeries χ`: they agree in the convergence range, but `LSeries χ s` is defined to be `0` if `re s ≤ 1`.χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.| 1ℝ/ 2ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.}
On \{\mathrm{Re}\, s > 1\} the by-parts series agrees with A (Dirichlet identification),
so e^{G} = e^{A+B+T} = L there by the Euler wiring. Both sides are holomorphic on \Omega
— G under the bounded-race hypothesis, and L because it is entire — and \Omega is open
and convex, hence preconnected. They agree on the open set \{\mathrm{Re}\, s > 1\}, which
contains a neighborhood of s = 2, so Mathlib's identity theorem for holomorphic functions
propagates the identity to all of \Omega. This is the only place the bounded-race hypothesis
converts into information below \mathrm{Re}\, s = 1. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hf : DifferentiableOn ℂ (fun s ↦ Complex.exp (contLog s)) {s : ℂ | 1 / 2 < s.re} :=
(differentiableOn_contLog hB).cexp
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hg : DifferentiableOn ℂ (DirichletCharacter.LFunction χ) {s : ℂ | 1 / 2 < s.re} :=
(DirichletCharacter.differentiable_LFunction χ_ne_one).differentiableOn
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hfg : Set.EqOn (fun s ↦ Complex.exp (contLog s)) (DirichletCharacter.LFunction χ)
{s : ℂ | 1 < s.re} := fun s hs ↦ exp_contLog_eq hB hs
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hΩ : IsOpen {s : ℂ | 1 / 2 < s.re} :=
isOpen_lt continuous_const Complex.continuous_re
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hO : IsOpen {s : ℂ | 1 < s.re} :=
isOpen_lt continuous_const Complex.continuous_re
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hpre : IsPreconnected {s : ℂ | 1 / 2 < s.re} :=
(convex_halfSpace_re_gt (1 / 2 : ℝ)).isPreconnected
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h₂O : (2 : ℂ) ∈ {s : ℂ | 1 < s.re} := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
norm_num`norm_num` normalizes numerical expressions in the goal. By default, it supports the operations
`+` `-` `*` `/` `⁻¹` `^` and `%` over types with (at least) an `AddMonoidWithOne` instance, such as
`ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ`. In addition to evaluating numerical expressions, `norm_num` will use `simp`
to simplify the goal. If the goal has the form `A = B`, `A ≠ B`, `A < B` or `A ≤ B`, where `A` and
`B` are numerical expressions, `norm_num` will try to close it. It also has a relatively simple
primality prover (available if you import `Mathlib.Tactic.NormNum.Prime`).
This tactic is extensible. Extensions can allow `norm_num` to evaluate more kinds of expressions, or
to prove more kinds of propositions (such as, primality of natural numbers). See the `@[norm_num]`
attribute for further information on extending `norm_num`.
* `norm_num at l` normalizes at location(s) `l`.
* `norm_num [h1, ...]` adds the arguments `h1, ...` to the `simp` set in addition to the default
`simp` set. All options for `simp` arguments are supported, in particular `←`, `↑` and `↓`.
* `norm_num only` does not use the default `simp` set for simplification. `norm_num only [h1, ...]`
uses only the arguments `h1, ...` in addition to the routines tagged `@[norm_num]`.
`norm_num only` still performs post-processing steps, like `simp only`, use `norm_num1` if you
exclusively want to normalize numerical expressions.
* `norm_num (config := cfg)` uses `cfg` as configuration for `simp` calls (see the `simp` tactic for
further details).
Examples:
```lean
example : 43 ≤ 74 + (33 : ℤ) := by norm_num
example : ¬ (7-2)/(2*3) ≥ (1:ℝ) + 2/(3^2) := by norm_num
```
[Set.mem_setOf_eq]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h₂Ω : (2 : ℂ) ∈ {s : ℂ | 1 / 2 < s.re} := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
norm_num`norm_num` normalizes numerical expressions in the goal. By default, it supports the operations
`+` `-` `*` `/` `⁻¹` `^` and `%` over types with (at least) an `AddMonoidWithOne` instance, such as
`ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ`. In addition to evaluating numerical expressions, `norm_num` will use `simp`
to simplify the goal. If the goal has the form `A = B`, `A ≠ B`, `A < B` or `A ≤ B`, where `A` and
`B` are numerical expressions, `norm_num` will try to close it. It also has a relatively simple
primality prover (available if you import `Mathlib.Tactic.NormNum.Prime`).
This tactic is extensible. Extensions can allow `norm_num` to evaluate more kinds of expressions, or
to prove more kinds of propositions (such as, primality of natural numbers). See the `@[norm_num]`
attribute for further information on extending `norm_num`.
* `norm_num at l` normalizes at location(s) `l`.
* `norm_num [h1, ...]` adds the arguments `h1, ...` to the `simp` set in addition to the default
`simp` set. All options for `simp` arguments are supported, in particular `←`, `↑` and `↓`.
* `norm_num only` does not use the default `simp` set for simplification. `norm_num only [h1, ...]`
uses only the arguments `h1, ...` in addition to the routines tagged `@[norm_num]`.
`norm_num only` still performs post-processing steps, like `simp only`, use `norm_num1` if you
exclusively want to normalize numerical expressions.
* `norm_num (config := cfg)` uses `cfg` as configuration for `simp` calls (see the `simp` tactic for
further details).
Examples:
```lean
example : 43 ≤ 74 + (33 : ℤ) := by norm_num
example : ¬ (7-2)/(2*3) ≥ (1:ℝ) + 2/(3^2) := by norm_num
```
[Set.mem_setOf_eq]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hev : (fun s ↦ Complex.exp (contLog s)) =ᶠ[𝓝 (2 : ℂ)]
DirichletCharacter.LFunction χ :=
Filter.eventuallyEq_of_mem (hO.mem_nhds h₂O) hfg
exact`exact e` closes the main goal if its target type matches that of `e`.
(hf.analyticOnNhd hΩ).eqOn_of_preconnected_of_eventuallyEq
(hg.analyticOnNhd hΩ) hpre h₂Ω hev
The continued logarithm. G := \tilde A_{f_\chi} + B + T, the sum of the by-parts
continuation of the k = 1 layer and the two explicit layers. The definition itself is
unconditional; under the bounded-race hypothesis, \tilde A_{f_\chi} is holomorphic on
\{\mathrm{Re}\, s > 0\} (its coefficient partial sums are exactly the race sums, bounded
by C), and B, T are holomorphic on \Omega = \{\mathrm{Re}\, s > 1/2\}
unconditionally — so G is holomorphic on \Omega, the largest half-plane the argument
needs.
Lean code for Definition contLog
-
⟲
def contLog (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.def contLog (s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.
⟲:= bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`:
`bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`.
If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this
series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it
agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re`
(`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series
to the right half-plane. This is the analytic-continuation object `Ã` of the proof. fChiA362583.fChi (n : ℕ) : ℂBridge: the ℕ-indexed coefficient function of `layerA` — `raceKernel n` at
primes, `0` elsewhere. Its partial sums are exactly `raceSum` (`sum_range_fChi`),
so `Complex.bpSeries fChi` is the by-parts continuation of `layerA`
(via `layerA_eq_tsum_fChi` + `Complex.tsum_mul_cpow_neg_eq_bpSeries`). sℂ + layerBA362583.layerB (s : ℂ) : ℂThe `k = 2` layer `B(s) = (1/2) Σ_{p odd} p^(-2s)`, with an `if`-mask at
`p = 2` (`χ(2)² = 0`, `χ(p)² = 1` for odd `p`; see `χ_sq_eq_ite`,
`tsum_term_two_eq_layerB`). Exponent convention: `-(2 * s)`. sℂ + layerTA362583.layerT (s : ℂ) : ℂThe `k ≥ 3` layer `T(s) = Σ_p t_p(s)`. sℂ
The Chebyshev race sum is the integer
S(N) \;=\; \sum_{\substack{p \le N \\ p \text{ prime}}} \begin{cases} +1 & p \equiv 1 \pmod 4 \\ -1 & p \equiv 3 \pmod 4 \\ \;\;\,0 & p = 2, \end{cases}
i.e. S(N) = \sum_{p \le N} \chi_4(p) — but stated with plain remainder arithmetic rather
than a character, per the statement-hygiene principle. The convention "primes \le N"
matches Mathlib's prime-counting function \pi(N) (Nat.primeCounting), which the race-sum
theorems quantify against.
Lean code for Definition raceSum
-
⟲
def raceSum (N
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℤInt : TypeThe integers. This type is special-cased by the compiler and overridden with an efficient implementation. The runtime has a special representation for `Int` that stores “small” signed numbers directly, while larger numbers use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)). A “small number” is an integer that can be encoded with one fewer bits than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit architectures).def raceSum (N
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℤInt : TypeThe integers. This type is special-cased by the compiler and overridden with an efficient implementation. The runtime has a special representation for `Int` that stores “small” signed numbers directly, while larger numbers use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)). A “small number” is an integer that can be encoded with one fewer bits than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit architectures).
⟲:= ∑ pℕ ∈ (Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`. (Nℕ + 1ℕ)).filterFinset.filter.{u_1} {α : Type u_1} (p : α → Prop) [DecidablePred p] (s : Finset α) : Finset α`Finset.filter p s` is the set of elements of `s` that satisfy `p`.
For example, one can use `s.filter (· ∈ t)` to get the intersection of `s` with `t : Set α`
as a `Finset α` (when a `DecidablePred (· ∈ t)` instance is available). Nat.PrimeNat.Prime (p : ℕ) : Prop`Nat.Prime p` means that `p` is a prime number, that is, a natural number
at least 2 whose only divisors are `p` and `1`.
The theorem `Nat.prime_def` witnesses this description of a prime number. , raceKernelA362583.raceKernel (n : ℕ) : ℤThe summand of the Chebyshev race sum: `+1` on `n ≡ 1 (mod 4)`, `-1` on
`n ≡ 3 (mod 4)`, `0` otherwise. At a prime `p` this is `χ₄(p)`. pℕ
The coefficient bridge. Define f_\chi : \mathbb{N} \to \C by f_\chi(n) = \kappa(n) if
n is prime and 0 otherwise — the race kernel restricted to the primes, packaged as an
\mathbb{N}-indexed coefficient sequence. The two exact identities developed in the lemmas
below feed f_\chi to the by-parts machinery.
Lean code for Definition fChi
-
⟲
def fChi (n
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.def fChi (n
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.
⟲:= if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
nℕ.PrimeNat.Prime (p : ℕ) : Prop`Nat.Prime p` means that `p` is a prime number, that is, a natural number
at least 2 whose only divisors are `p` and `1`.
The theorem `Nat.prime_def` witnesses this description of a prime number. then`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
((raceKernelA362583.raceKernel (n : ℕ) : ℤThe summand of the Chebyshev race sum: `+1` on `n ≡ 1 (mod 4)`, `-1` on
`n ≡ 3 (mod 4)`, `0` otherwise. At a prime `p` this is `χ₄(p)`. nℕ : ℤInt : TypeThe integers.
This type is special-cased by the compiler and overridden with an efficient implementation. The
runtime has a special representation for `Int` that stores “small” signed numbers directly, while
larger numbers use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)). A “small number” is an integer that can be encoded with one fewer bits
than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit
architectures).
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) else`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
0ℂ
Continuity at 1/2. There are constants M_0 > 0 and \delta_0 > 0 with
\|L(s, \chi)\| < M_0 for every s with \|s - 1/2\| < \delta_0. No hypothesis on the
race is used: the bound comes from the entirety of L(\cdot, \chi) alone.
Lean code for Lemma exists_norm_LFunction_lt_near_half
-
⟲
theorem exists_norm_LFunction_lt_near_half : ∃ M₀
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., 0ℝ< M₀ℝ∧ ∃ δ₀ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., 0ℝ< δ₀ℝ∧ ∀ sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`., ‖sℂ- ((1ℝ/ 2ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)‖ < δ₀ℝ→ ‖DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions. Note that this is not the same as `LSeries χ`: they agree in the convergence range, but `LSeries χ s` is defined to be `0` if `re s ≤ 1`.χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.sℂ‖ < M₀ℝtheorem exists_norm_LFunction_lt_near_half : ∃ M₀
ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., 0ℝ< M₀ℝ∧ ∃ δ₀ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., 0ℝ< δ₀ℝ∧ ∀ sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`., ‖sℂ- ((1ℝ/ 2ℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.)‖ < δ₀ℝ→ ‖DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions. Note that this is not the same as `LSeries χ`: they agree in the convergence range, but `LSeries χ s` is defined to be `0` if `re s ≤ 1`.χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.sℂ‖ < M₀ℝ
Since \chi is nonprincipal, L(\cdot, \chi) is entire, hence continuous at 1/2. Taking
M_0 := \|L(1/2, \chi)\| + 1, continuity supplies a radius \delta_0 > 0 on which
\|L(s, \chi)\| < M_0. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hcontContinuousAt (DirichletCharacter.LFunction χ) ↑(1 / 2) : ContinuousAtContinuousAt.{u_1, u_2} {X : Type u_1} {Y : Type u_2} [TopologicalSpace X] [TopologicalSpace Y] (f : X → Y) (x : X) :
PropA function between topological spaces is continuous at a point `x₀`
if `f x` tends to `f x₀` when `x` tends to `x₀`. (DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ) ((1ℝ / 2ℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) :=
(DirichletCharacter.differentiable_LFunctionDirichletCharacter.differentiable_LFunction {N : ℕ} [NeZero N] {χ : DirichletCharacter ℂ N} (hχ : χ ≠ 1) :
Differentiable ℂ (DirichletCharacter.LFunction χ)The L-function of a non-trivial Dirichlet character is differentiable everywhere. χ_ne_oneA362583.χ_ne_one : χ ≠ 1`χ ≠ 1` (they differ at `3`, a unit of `ZMod 4`). ).continuousDifferentiable.continuous.{u_1, u_2, u_3} {𝕜 : Type u_1} [NontriviallyNormedField 𝕜] {E : Type u_2} [AddCommGroup E]
[Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F] {f : E → F}
[ContinuousAdd E] [ContinuousSMul 𝕜 E] [ContinuousAdd F] [ContinuousSMul 𝕜 F] (h : Differentiable 𝕜 f) : Continuous f.continuousAtContinuous.continuousAt.{u_1, u_2} {X : Type u_1} {Y : Type u_2} [TopologicalSpace X] [TopologicalSpace Y] {f : X → Y}
{x : X} (h : Continuous f) : ContinuousAt f x
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨δ₀ℝ, hδ₀posδ₀ > 0, hδ₀∀ ⦃x : ℂ⦄, dist x ↑(1 / 2) < δ₀ → dist (DirichletCharacter.LFunction χ x) (DirichletCharacter.LFunction χ ↑(1 / 2)) < 1⟩ := Metric.continuousAt_iffMetric.continuousAt_iff.{u, v} {α : Type u} {β : Type v} [PseudoMetricSpace α] [PseudoMetricSpace β] {f : α → β}
{a : α} : ContinuousAt f a ↔ ∀ ε > 0, ∃ δ > 0, ∀ ⦃x : α⦄, dist x a < δ → dist (f x) (f a) < ε.mpIff.mp {a b : Prop} (self : a ↔ b) : a → bModus ponens for if and only if. If `a ↔ b` and `a`, then `b`. hcontContinuousAt (DirichletCharacter.LFunction χ) ↑(1 / 2) 1ℝ one_posone_pos.{u_1} {α : Type u_1} [Zero α] [One α] [PartialOrder α] [ZeroLEOneClass α] [NeZero 1] : 0 < 1**Alias** of `zero_lt_one`.
---
See `zero_lt_one'` for a version with the type explicit.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
⟨Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. ‖DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((1ℝ / 2ℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )‖ + 1ℝ,Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`.
add_pos_of_nonneg_of_posadd_pos_of_nonneg_of_pos.{u_1} {α : Type u_1} [AddZeroClass α] [Preorder α] [AddLeftStrictMono α] {a b : α} (ha : 0 ≤ a)
(hb : 0 < b) : 0 < a + b**Alias** of `Left.add_pos_of_nonneg_of_pos`. (norm_nonnegnorm_nonneg.{u_5} {E : Type u_5} [SeminormedAddGroup E] (a : E) : 0 ≤ ‖a‖ _) one_posone_pos.{u_1} {α : Type u_1} [Zero α] [One α] [PartialOrder α] [ZeroLEOneClass α] [NeZero 1] : 0 < 1**Alias** of `zero_lt_one`.
---
See `zero_lt_one'` for a version with the type explicit. ,Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. δ₀ℝ,Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. hδ₀posδ₀ > 0,Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. fun sℂ hs‖s - ↑(1 / 2)‖ < δ₀ ↦ ?_⟩Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists pExistential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hddist (DirichletCharacter.LFunction χ s) (DirichletCharacter.LFunction χ ↑(1 / 2)) < 1 : distDist.dist.{u_3} {α : Type u_3} [self : Dist α] : α → α → ℝDistance between two points (DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. sℂ)
(DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((1ℝ / 2ℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )) < 1ℝ :=
hδ₀∀ ⦃x : ℂ⦄, dist x ↑(1 / 2) < δ₀ → dist (DirichletCharacter.LFunction χ x) (DirichletCharacter.LFunction χ ↑(1 / 2)) < 1 (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. rwa`rwa` is short-hand for `rw; assumption`. [dist_eq_normdist_eq_norm.{u_5} {E : Type u_5} [SeminormedAddCommGroup E] (a b : E) : dist a b = ‖a - b‖**Alias** of `dist_eq_norm_sub`.])
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[dist_eq_normdist_eq_norm.{u_5} {E : Type u_5} [SeminormedAddCommGroup E] (a b : E) : dist a b = ‖a - b‖**Alias** of `dist_eq_norm_sub`.] atLocation specifications are used by many tactics that can operate on either the
hypotheses or the goal. It can have one of the forms:
* 'empty' is not actually present in this syntax, but most tactics use
`(location)?` matchers. It means to target the goal only.
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
* `at *`: target all hypotheses and the goal
hddist (DirichletCharacter.LFunction χ s) (DirichletCharacter.LFunction χ ↑(1 / 2)) < 1
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hn‖DirichletCharacter.LFunction χ s‖ - ‖DirichletCharacter.LFunction χ ↑(1 / 2)‖ ≤
‖DirichletCharacter.LFunction χ s - DirichletCharacter.LFunction χ ↑(1 / 2)‖ := norm_sub_norm_lenorm_sub_norm_le.{u_5} {E : Type u_5} [SeminormedAddCommGroup E] (a b : E) : ‖a‖ - ‖b‖ ≤ ‖a - b‖ (DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. sℂ)
(DirichletCharacter.LFunctionDirichletCharacter.LFunction {N : ℕ} [NeZero N] (χ : DirichletCharacter ℂ N) (s : ℂ) : ℂThe unique meromorphic function `ℂ → ℂ` which agrees with `∑' n : ℕ, χ n / n ^ s` wherever the
latter is convergent. This is constructed as a linear combination of Hurwitz zeta functions.
Note that this is not the same as `LSeries χ`: they agree in the convergence range, but
`LSeries χ s` is defined to be `0` if `re s ≤ 1`.
χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((1ℝ / 2ℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ))
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
Per-prime split. For \mathrm{Re}\, s > 1 and each prime p, with
z_p = \chi(p) p^{-s} (so \|z_p\| \le 2^{-\sigma} < 1/2),
-\operatorname{Log}\bigl(1 - z_p\bigr) \;=\; \chi(p)\, p^{-s} \;+\; \tfrac12\, \chi(p)^2\, p^{-2s} \;+\; t_p(s).
Lean code for Lemma neg_log_split
-
⟲
theorem neg_log_split (p
Nat.Primes: Nat.PrimesNat.Primes : TypeThe type of prime numbers) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs1 < s.re: 1ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : -Complex.logComplex.log (x : ℂ) : ℂInverse of the `exp` function. Returns values such that `(log x).im > - π` and `(log x).im ≤ π`. `log 0 = 0`(1ℂ- χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`.4ℕ) * ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ)) = χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`.4ℕ) * ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ) + χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`.4ℕ) ^ 2ℕ* ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-(2ℂ* sℂ)) / 2ℂ+ tpA362583.tp (p : Nat.Primes) (s : ℂ) : ℂPer-prime `k ≥ 3` tail `t_p(s) = Σ_{k≥0} (χ(p) p^(-s))^(k+3) / (k+3)`. The compact form `z_p^(k+3)/(k+3)` (rather than the expanded `χ(p)^(k+3) p^(-(k+3)s)/(k+3)`) is chosen so that the tail produced by peeling three terms off `hasSum_neg_log_one_sub` is *definitionally* `tp p s` (see `neg_log_split`).pNat.Primessℂtheorem neg_log_split (p
Nat.Primes: Nat.PrimesNat.Primes : TypeThe type of prime numbers) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs1 < s.re: 1ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : -Complex.logComplex.log (x : ℂ) : ℂInverse of the `exp` function. Returns values such that `(log x).im > - π` and `(log x).im ≤ π`. `log 0 = 0`(1ℂ- χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`.4ℕ) * ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ)) = χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`.4ℕ) * ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ) + χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`.((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`.4ℕ) ^ 2ℕ* ((pNat.Primes: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-(2ℂ* sℂ)) / 2ℂ+ tpA362583.tp (p : Nat.Primes) (s : ℂ) : ℂPer-prime `k ≥ 3` tail `t_p(s) = Σ_{k≥0} (χ(p) p^(-s))^(k+3) / (k+3)`. The compact form `z_p^(k+3)/(k+3)` (rather than the expanded `χ(p)^(k+3) p^(-(k+3)s)/(k+3)`) is chosen so that the tail produced by peeling three terms off `hasSum_neg_log_one_sub` is *definitionally* `tp p s` (see `neg_log_split`).pNat.Primessℂ
Since \|z_p\| \le 2^{-\sigma} < 1, the logarithm series applies:
-\operatorname{Log}(1 - z_p) = \sum_{k \ge 1} z_p^{\,k}/k. Peel off the first three terms.
The k = 0 term is 0; k = 1 gives z_p = \chi(p)\, p^{-s}; k = 2 gives
z_p^2/2 = \tfrac12\, \chi(p)^2\, p^{-2s}; and the remaining tail
\sum_{k \ge 0} z_p^{\,k+3}/(k+3) is definitionally t_p(s) — that is the reason for the
exact shape of the definition of t_p. Splitting finitely many terms off one convergent
series is not a rearrangement, and summing this identity over p (next chapter) uses only
linearity of three convergent series, so the double-sum rearrangement of the original informal
argument is avoided entirely. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hz1‖χ ↑↑p * ↑↑p ^ (-s)‖ < 1 : ‖χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)‖ < 1ℝ :=
lt_of_le_of_ltlt_of_le_of_lt.{u_1} {α : Type u_1} [Preorder α] {a b c : α} (hab : a ≤ b) (hbc : b < c) : a < c (norm_χ_mul_cpow_le_halfA362583.norm_χ_mul_cpow_le_half (p : Nat.Primes) {s : ℂ} (hs : 1 ≤ s.re) : ‖χ ↑↑p * ↑↑p ^ (-s)‖ ≤ 1 / 2`‖χ(p) p^(-s)‖ ≤ 1/2` on `Re s ≥ 1` (the domain of the per-prime split). pNat.Primes hs1 < s.re.leLT.lt.le.{u_1} {α : Type u_1} [Preorder α] {a b : α} (hab : a < b) : a ≤ b**Alias** of `le_of_lt`.) (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. norm_num`norm_num` normalizes numerical expressions in the goal. By default, it supports the operations
`+` `-` `*` `/` `⁻¹` `^` and `%` over types with (at least) an `AddMonoidWithOne` instance, such as
`ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ`. In addition to evaluating numerical expressions, `norm_num` will use `simp`
to simplify the goal. If the goal has the form `A = B`, `A ≠ B`, `A < B` or `A ≤ B`, where `A` and
`B` are numerical expressions, `norm_num` will try to close it. It also has a relatively simple
primality prover (available if you import `Mathlib.Tactic.NormNum.Prime`).
This tactic is extensible. Extensions can allow `norm_num` to evaluate more kinds of expressions, or
to prove more kinds of propositions (such as, primality of natural numbers). See the `@[norm_num]`
attribute for further information on extending `norm_num`.
* `norm_num at l` normalizes at location(s) `l`.
* `norm_num [h1, ...]` adds the arguments `h1, ...` to the `simp` set in addition to the default
`simp` set. All options for `simp` arguments are supported, in particular `←`, `↑` and `↓`.
* `norm_num only` does not use the default `simp` set for simplification. `norm_num only [h1, ...]`
uses only the arguments `h1, ...` in addition to the routines tagged `@[norm_num]`.
`norm_num only` still performs post-processing steps, like `simp only`, use `norm_num1` if you
exclusively want to normalize numerical expressions.
* `norm_num (config := cfg)` uses `cfg` as configuration for `simp` calls (see the `simp` tactic for
further details).
Examples:
```lean
example : 43 ≤ 74 + (33 : ℤ) := by norm_num
example : ¬ (7-2)/(2*3) ≥ (1:ℝ) + 2/(3^2) := by norm_num
```
)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hHasSum (fun k => (χ ↑↑p * ↑↑p ^ (-s)) ^ k / ↑k) (-Complex.log (1 - χ ↑↑p * ↑↑p ^ (-s))) := hasSum_neg_log_one_subA362583.hasSum_neg_log_one_sub {z : ℂ} (hz : ‖z‖ < 1) : HasSum (fun k => z ^ k / ↑k) (-Complex.log (1 - z))`-log(1-z) = Σ_{k≥1} z^k/k` in `HasSum` form. The `k = 0` term is `0`
(Lean's `z^0/0 = 0`). Thin wrapper around `Complex.hasSum_taylorSeries_neg_log`;
the branch of `Complex.log` is pinned inside Mathlib's proof, so no separate
exp-inversion argument is needed. hz1‖χ ↑↑p * ↑↑p ^ (-s)‖ < 1
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[← hHasSum (fun k => (χ ↑↑p * ↑↑p ^ (-s)) ^ k / ↑k) (-Complex.log (1 - χ ↑↑p * ↑↑p ^ (-s))).tsum_eqHasSum.tsum_eq.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] {L : SummationFilter β}
{f : β → α} {a : α} [T2Space α] [L.NeBot] (ha : HasSum f a L) : ∑'[L] (b : β), f b = a, ← hHasSum (fun k => (χ ↑↑p * ↑↑p ^ (-s)) ^ k / ↑k) (-Complex.log (1 - χ ↑↑p * ↑↑p ^ (-s))).summableHasSum.summable.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] {L : SummationFilter β}
{f : β → α} {a : α} (h : HasSum f a L) : Summable f L.sum_add_tsum_nat_addSummable.sum_add_tsum_nat_add.{u_2} {G : Type u_2} [AddCommGroup G] [TopologicalSpace G] [IsTopologicalAddGroup G]
[T2Space G] {f : ℕ → G} (k : ℕ) (h : Summable f) : ∑ i ∈ Finset.range k, f i + ∑' (i : ℕ), f (i + k) = ∑' (i : ℕ), f i 3ℕ, Finset.sum_range_succFinset.sum_range_succ.{u_4} {M : Type u_4} [AddCommMonoid M] (f : ℕ → M) (n : ℕ) :
∑ x ∈ Finset.range (n + 1), f x = ∑ x ∈ Finset.range n, f x + f n,
Finset.sum_range_succFinset.sum_range_succ.{u_4} {M : Type u_4} [AddCommMonoid M] (f : ℕ → M) (n : ℕ) :
∑ x ∈ Finset.range (n + 1), f x = ∑ x ∈ Finset.range n, f x + f n, Finset.sum_range_oneFinset.sum_range_one.{u_4} {M : Type u_4} [AddCommMonoid M] (f : ℕ → M) : ∑ k ∈ Finset.range 1, f k = f 0]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h2(χ ↑↑p * ↑↑p ^ (-s)) ^ 2 = χ ↑↑p ^ 2 * ↑↑p ^ (-(2 * s)) : (χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)) ^ 2ℕ
= χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) ^ 2ℕ * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-(2ℂ * sℂ)) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[mul_powmul_pow.{u_4} {M : Type u_4} [CommMonoid M] (a b : M) (n : ℕ) : (a * b) ^ n = a ^ n * b ^ n, ← Complex.cpow_nat_mulComplex.cpow_nat_mul (x : ℂ) (n : ℕ) (y : ℂ) : x ^ (↑n * y) = (x ^ y) ^ n]
congrApply congruence (recursively) to goals of the form `⊢ f as = f bs` and `⊢ f as ≍ f bs`.
The optional parameter is the depth of the recursive applications.
This is useful when `congr` is too aggressive in breaking down the goal.
For example, given `⊢ f (g (x + y)) = f (g (y + x))`,
`congr` produces the goals `⊢ x = y` and `⊢ y = x`,
while `congr 2` produces the intended `⊢ x + y = y + x`.
2
push_cast`push_cast` rewrites the goal to move certain coercions (*casts*) inward, toward the leaf nodes.
This uses `norm_cast` lemmas in the forward direction.
For example, `↑(a + b)` will be written to `↑a + ↑b`.
- `push_cast` moves casts inward in the goal.
- `push_cast at h` moves casts inward in the hypothesis `h`.
It can be used with extra simp lemmas with, for example, `push_cast [Int.add_zero]`.
Example:
```lean
example (a b : Nat)
(h1 : ((a + b : Nat) : Int) = 10)
(h2 : ((a + b + 0 : Nat) : Int) = 10) :
((a + b : Nat) : Int) = 10 := by
/-
h1 : ↑(a + b) = 10
h2 : ↑(a + b + 0) = 10
⊢ ↑(a + b) = 10
-/
push_cast
/- Now
⊢ ↑a + ↑b = 10
-/
push_cast at h1
push_cast [Int.add_zero] at h2
/- Now
h1 h2 : ↑a + ↑b = 10
-/
exact h1
```
See also `norm_cast`.
ring`ring` solves equations in *commutative* (semi)rings, allowing for variables in the
exponent. If the goal is not appropriate for `ring` (e.g. not an equality) `ring_nf` will be
suggested. See also `ring1`, which fails if the goal is not an equality.
* `ring!` will use a more aggressive reducibility setting to determine equality of atoms.
Examples:
```
example (n : ℕ) (m : ℤ) : 2^(n+1) * m = 2 * 2^n * m := by ring
example (a b : ℤ) (n : ℕ) : (a + b)^(n + 2) = (a^2 + b^2 + a * b + b * a) * (a + b)^n := by ring
example (x y : ℕ) : x + id y = y + id x := by ring!
example (x : ℕ) (h : x * 2 > 5): x + x > 5 := by ring; assumption -- suggests ring_nf
```
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
htail∑' (k : ℕ), (χ ↑↑p * ↑↑p ^ (-s)) ^ (k + 3) / ↑(k + 3) = tp p s : ∑' kℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
, (χA362583.χ : DirichletCharacter ℂ 4The project Dirichlet character mod 4, `ZMod.χ₄` pushed to `ℂ`. ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ZModZMod : ℕ → TypeThe integers modulo `n : ℕ`. 4ℕ) * ((pNat.Primes : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ)) ^ (kℕ + 3ℕ)
/ ((kℕ + 3ℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) = tpA362583.tp (p : Nat.Primes) (s : ℂ) : ℂPer-prime `k ≥ 3` tail `t_p(s) = Σ_{k≥0} (χ(p) p^(-s))^(k+3) / (k+3)`.
The compact form `z_p^(k+3)/(k+3)` (rather than the expanded
`χ(p)^(k+3) p^(-(k+3)s)/(k+3)`) is chosen so that the tail produced by peeling three
terms off `hasSum_neg_log_one_sub` is *definitionally* `tp p s` (see `neg_log_split`). pNat.Primes sℂ := rflrfl.{u} {α : Sort u} {a : α} : a = a`rfl : a = a` is the unique constructor of the equality type. This is the
same as `Eq.refl` except that it takes `a` implicitly instead of explicitly.
This is a more powerful theorem than it may appear at first, because although
the statement of the theorem is `a = a`, Lean will allow anything that is
definitionally equal to that type. So, for instance, `2 + 2 = 4` is proven in
Lean by `rfl`, because both sides are the same up to definitional equality.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[h2(χ ↑↑p * ↑↑p ^ (-s)) ^ 2 = χ ↑↑p ^ 2 * ↑↑p ^ (-(2 * s)), htail∑' (k : ℕ), (χ ↑↑p * ↑↑p ^ (-s)) ^ (k + 3) / ↑(k + 3) = tp p s]
norm_num`norm_num` normalizes numerical expressions in the goal. By default, it supports the operations
`+` `-` `*` `/` `⁻¹` `^` and `%` over types with (at least) an `AddMonoidWithOne` instance, such as
`ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ`. In addition to evaluating numerical expressions, `norm_num` will use `simp`
to simplify the goal. If the goal has the form `A = B`, `A ≠ B`, `A < B` or `A ≤ B`, where `A` and
`B` are numerical expressions, `norm_num` will try to close it. It also has a relatively simple
primality prover (available if you import `Mathlib.Tactic.NormNum.Prime`).
This tactic is extensible. Extensions can allow `norm_num` to evaluate more kinds of expressions, or
to prove more kinds of propositions (such as, primality of natural numbers). See the `@[norm_num]`
attribute for further information on extending `norm_num`.
* `norm_num at l` normalizes at location(s) `l`.
* `norm_num [h1, ...]` adds the arguments `h1, ...` to the `simp` set in addition to the default
`simp` set. All options for `simp` arguments are supported, in particular `←`, `↑` and `↓`.
* `norm_num only` does not use the default `simp` set for simplification. `norm_num only [h1, ...]`
uses only the arguments `h1, ...` in addition to the routines tagged `@[norm_num]`.
`norm_num only` still performs post-processing steps, like `simp only`, use `norm_num1` if you
exclusively want to normalize numerical expressions.
* `norm_num (config := cfg)` uses `cfg` as configuration for `simp` calls (see the `simp` tactic for
further details).
Examples:
```lean
example : 43 ≤ 74 + (33 : ℤ) := by norm_num
example : ¬ (7-2)/(2*3) ≥ (1:ℝ) + 2/(3^2) := by norm_num
```
Increment bound. For s \in \C with -1 \le \mathrm{Re}\, s and a
natural number n \ge 1,
\bigl\| n^{-s} - (n+1)^{-s} \bigr\| \;\le\; \|s\|\; n^{-\mathrm{Re}\,s - 1}.
Lean code for Lemma Complex.norm_natCast_cpow_sub_add_one_cpow_le
-
⟲
theorem norm_natCast_cpow_sub_add_one_cpow_le {s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs-1 ≤ s.re: -1ℝ≤ sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) {nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn1 ≤ n: 1ℕ≤ nℕ) : ‖(nℕ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ) - ((nℕ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) + 1ℂ) ^ (-sℂ)‖ ≤ ‖sℂ‖ * (nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) ^ (-sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.- 1ℝ)theorem norm_natCast_cpow_sub_add_one_cpow_le {s
ℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs-1 ≤ s.re: -1ℝ≤ sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) {nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn1 ≤ n: 1ℕ≤ nℕ) : ‖(nℕ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ) - ((nℕ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) + 1ℂ) ^ (-sℂ)‖ ≤ ‖sℂ‖ * (nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) ^ (-sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.- 1ℝ)
The bound is the special case r = -s, [a, b] = [n, n+1] (so b - a = 1) of the general
estimate
\bigl\| b^{r} - a^{r} \bigr\| \;\le\; \|r\|\,(b - a)\, a^{\mathrm{Re}\,r - 1} \qquad (0 < a \le b,\ \ \mathrm{Re}\,r \le 1);
here \mathrm{Re}(-s) = -\mathrm{Re}\, s \le 1 because -1 \le \mathrm{Re}\, s.
The estimate is the only genuinely analytic ingredient of the chapter, and it is a single
application of the fundamental theorem of calculus. Since t \mapsto t^{r} has derivative
r\, t^{r-1},
b^{r} - a^{r} \;=\; \int_a^b r\, t^{r-1}\, \mathrm{d}t.
On [a, b] the integrand has norm
\|r\|\, t^{\mathrm{Re}\,r - 1} \le \|r\|\, a^{\mathrm{Re}\,r - 1},
because t \mapsto t^{\mathrm{Re}\,r - 1} is antitone when \mathrm{Re}\,r - 1 \le 0;
bounding the integral of this constant majorant over an interval of length b - a gives the
estimate. Everything downstream is then index bookkeeping, telescoping, and a Weierstrass
M-test. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hn'0 < ↑n : (0ℝ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) < (nℕ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) := Nat.cast_posNat.cast_pos.{u_3} {α : Type u_3} [Semiring α] [PartialOrder α] [IsOrderedRing α] [Nontrivial α] {n : ℕ} :
0 < ↑n ↔ 0 < nSpecialisation of `Nat.cast_pos'`, which seems to be easier for Lean to use. .2Iff.mpr {a b : Prop} (self : a ↔ b) : b → aModus ponens for if and only if, reversed. If `a ↔ b` and `b`, then `a`. hn1 ≤ n
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h‖↑(↑n + 1) ^ (-s) - ↑↑n ^ (-s)‖ ≤ ‖-s‖ * (↑n + 1 - ↑n) * ↑n ^ ((-s).re - 1) := norm_ofReal_cpow_sub_ofReal_cpow_leComplex.norm_ofReal_cpow_sub_ofReal_cpow_le {r : ℂ} (hr : r.re ≤ 1) {a b : ℝ} (ha : 0 < a) (hab : a ≤ b) :
‖↑b ^ r - ↑a ^ r‖ ≤ ‖r‖ * (b - a) * a ^ (r.re - 1)**FTC increment bound** for complex powers of positive reals: if `r.re ≤ 1` and
`0 < a ≤ b`, then `‖(b : ℂ) ^ r - (a : ℂ) ^ r‖ ≤ ‖r‖ * (b - a) * a ^ (r.re - 1)`.
The difference is the integral of the derivative `r * x ^ (r - 1)`, whose norm on `[a, b]`
is at most `‖r‖ * a ^ (r.re - 1)` because `x ↦ x ^ (r.re - 1)` is antitone for
`r.re - 1 ≤ 0`. (r := -sℂ)
(by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[neg_reComplex.neg_re (z : ℂ) : (-z).re = -z.re]; linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
) hn'0 < ↑n (le_add_of_nonneg_rightle_add_of_nonneg_right.{u_1} {α : Type u_1} [AddZeroClass α] [LE α] [AddLeftMono α] {a b : α} (h : 0 ≤ b) : a ≤ a + b zero_le_onezero_le_one.{u_1} {α : Type u_1} [Zero α] [One α] [LE α] [ZeroLEOneClass α] : 0 ≤ 1`zero_le_one` with the type argument implicit. )
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[norm_sub_revnorm_sub_rev.{u_5} {E : Type u_5} [SeminormedAddGroup E] (a b : E) : ‖a - b‖ = ‖b - a‖] atLocation specifications are used by many tactics that can operate on either the
hypotheses or the goal. It can have one of the forms:
* 'empty' is not actually present in this syntax, but most tactics use
`(location)?` matchers. It means to target the goal only.
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
* `at *`: target all hypotheses and the goal
h‖↑(↑n + 1) ^ (-s) - ↑↑n ^ (-s)‖ ≤ ‖-s‖ * (↑n + 1 - ↑n) * ↑n ^ ((-s).re - 1)
simpaThis is a "finishing" tactic modification of `simp`. It has two forms.
* `simpa [rules, ⋯] using e` will simplify the goal and the type of
`e` using `rules`, then try to close the goal using `e`.
Simplifying the type of `e` makes it more likely to match the goal
(which has also been simplified). This construction also tends to be
more robust under changes to the simp lemma set.
The final match between the simplified `e` and the simplified goal uses
**reducible** transparency, so it does not unfold semireducible definitions.
Write `simpa [rules, ⋯] using! e` to perform the match at the ambient
(default/semireducible) transparency instead.
* `simpa [rules, ⋯]` will simplify the goal and the type of a
hypothesis `this` if present in the context, then try to close the goal using
the `assumption` tactic.
As with `simp`, the `!` modifier after `simpa` enables auto-unfolding of
definitions in the simp set.
[add_sub_cancel_leftadd_sub_cancel_left.{u_3} {G : Type u_3} [AddCommGroup G] (a b : G) : a + b - a = b] usingThe arguments to the `simpa` family tactics. h‖↑↑n ^ (-s) - ↑(↑n + 1) ^ (-s)‖ ≤ ‖-s‖ * (↑n + 1 - ↑n) * ↑n ^ ((-s).re - 1)
The mod-4 character. \chi is Mathlib's \chi_4 pushed into \C as a Dirichlet
character mod 4; it is nonprincipal (\chi \ne 1, witnessed at 3). The bridge
\chi(n) = \kappa(n) for every natural n keeps the elementary statements — phrased with
the race kernel \kappa — aligned with the character world the analysis lives in.
Lean code for Definition χ
-
⟲
def χ : DirichletCharacter
DirichletCharacter.{u_1} (R : Type u_1) [CommMonoidWithZero R] (n : ℕ) : Type u_1The type of Dirichlet characters of level `n`.ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.4ℕdef χ : DirichletCharacter
DirichletCharacter.{u_1} (R : Type u_1) [CommMonoidWithZero R] (n : ℕ) : Type u_1The type of Dirichlet characters of level `n`.ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.4ℕ
⟲:= ZMod.χ₄ZMod.χ₄ : MulChar (ZMod 4) ℤDefine the nontrivial quadratic character on `ZMod 4`, `χ₄`.
It corresponds to the extension `ℚ(√-1)/ℚ`. .ringHomCompMulChar.ringHomComp.{u_1, u_2, u_3} {R : Type u_1} [CommMonoid R] {R' : Type u_2} [CommRing R'] {R'' : Type u_3}
[CommRing R''] (χ : MulChar R R') (f : R' →+* R'') : MulChar R R''We can post-compose a multiplicative character with a ring homomorphism. (Int.castRingHomInt.castRingHom.{u_3} (α : Type u_3) [NonAssocRing α] : ℤ →+* α`coe : ℤ → α` as a `RingHom`. ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )
Term holomorphy. For \delta > 0 and every n, the by-parts term
s \mapsto \bigl(\sum_{k \le n} f(k)\bigr)\bigl(n^{-s} - (n+1)^{-s}\bigr) is holomorphic on the
box \{\mathrm{Re}\, s > \delta\} \cap B(0, R).
Lean code for Lemma differentiableOn_bpSeries_term
-
⌁
theorem differentiableOn_bpSeries_term
differentiableOn_bpSeries_term {f : ℕ → ℂ} {R δ : ℝ} (hδ : 0 < δ) (n : ℕ) : DifferentiableOn ℂ (fun s ↦ (∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-s) - (↑n + 1) ^ (-s))) ({s | δ < s.re} ∩ Metric.ball 0 R)**Per-term holomorphy on the box**: each by-parts term `(∑ k ≤ n, f k) · ((n : ℂ) ^ (-s) - (n + 1) ^ (-s))` is holomorphic on `{s | δ < s.re} ∩ ball 0 R` — a constant when `n = 0`, a difference of complex powers otherwise.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Rℝδℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hδ0 < δ: 0 <LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.δℝ) (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : DifferentiableOnDifferentiableOn.{u_1, u_2, u_3} (𝕜 : Type u_1) [NontriviallyNormedField 𝕜] {E : Type u_2} [AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F] (f : E → F) (s : Set E) : Prop`DifferentiableOn 𝕜 f s` means that `f` is differentiable within `s` at any point of `s`.ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.(fun sℂ↦ (∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).sℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).sℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).) (Inter.inter.{u} {α : Type u} [self : Inter α] : α → α → α`a ∩ b` is the intersection of `a` and `b`. Conventions for notations in identifiers: * The recommended spelling of `∩` in identifiers is `inter`.{setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`sℂ|setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`δℝ<LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.}setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`∩Inter.inter.{u} {α : Type u} [self : Inter α] : α → α → α`a ∩ b` is the intersection of `a` and `b`. Conventions for notations in identifiers: * The recommended spelling of `∩` in identifiers is `inter`.Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε`0 Rℝ)Inter.inter.{u} {α : Type u} [self : Inter α] : α → α → α`a ∩ b` is the intersection of `a` and `b`. Conventions for notations in identifiers: * The recommended spelling of `∩` in identifiers is `inter`.theorem differentiableOn_bpSeries_term
differentiableOn_bpSeries_term {f : ℕ → ℂ} {R δ : ℝ} (hδ : 0 < δ) (n : ℕ) : DifferentiableOn ℂ (fun s ↦ (∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-s) - (↑n + 1) ^ (-s))) ({s | δ < s.re} ∩ Metric.ball 0 R)**Per-term holomorphy on the box**: each by-parts term `(∑ k ≤ n, f k) · ((n : ℂ) ^ (-s) - (n + 1) ^ (-s))` is holomorphic on `{s | δ < s.re} ∩ ball 0 R` — a constant when `n = 0`, a difference of complex powers otherwise.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Rℝδℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hδ0 < δ: 0 <LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.δℝ) (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : DifferentiableOnDifferentiableOn.{u_1, u_2, u_3} (𝕜 : Type u_1) [NontriviallyNormedField 𝕜] {E : Type u_2} [AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E] {F : Type u_3} [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace F] (f : E → F) (s : Set E) : Prop`DifferentiableOn 𝕜 f s` means that `f` is differentiable within `s` at any point of `s`.ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.(fun sℂ↦ (∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).sℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).sℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).) (Inter.inter.{u} {α : Type u} [self : Inter α] : α → α → α`a ∩ b` is the intersection of `a` and `b`. Conventions for notations in identifiers: * The recommended spelling of `∩` in identifiers is `inter`.{setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`sℂ|setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`δℝ<LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.}setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`∩Inter.inter.{u} {α : Type u} [self : Inter α] : α → α → α`a ∩ b` is the intersection of `a` and `b`. Conventions for notations in identifiers: * The recommended spelling of `∩` in identifiers is `inter`.Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε`0 Rℝ)Inter.inter.{u} {α : Type u} [self : Inter α] : α → α → α`a ∩ b` is the intersection of `a` and `b`. Conventions for notations in identifiers: * The recommended spelling of `∩` in identifiers is `inter`.
A finite constant multiple of a difference of the entire maps s \mapsto n^{-s} and
s \mapsto (n+1)^{-s}. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rcases`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
Nat.eq_zero_or_pos n with`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
rfl | hn
· -- `n = 0`: on the box the term is the constant `(f 0) * (0 - 1)`
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
(differentiableOn_const ((∑ k ∈ Finset.range (0 + 1), f k) * (0 - 1))).congr ?_
introIntroduces one or more hypotheses, optionally naming and/or pattern-matching them.
For each hypothesis to be introduced, the remaining main goal's target type must
be a `let` or function type.
* `intro` by itself introduces one anonymous hypothesis, which can be accessed
by e.g. `assumption`. It is equivalent to `intro _`.
* `intro x y` introduces two hypotheses and names them. Individual hypotheses
can be anonymized via `_`, given a type ascription, or matched against a pattern:
```lean
-- ... ⊢ α × β → ...
intro (a, b)
-- ..., a : α, b : β ⊢ ...
```
* `intro rfl` is short for `intro h; subst h`, if `h` is an equality where the left-hand or right-hand side
is a variable.
* Alternatively, `intro` can be combined with pattern matching much like `fun`:
```lean
intro
| n + 1, 0 => tac
| ...
```
w hw
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hw0 : -w ≠ 0 := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[ne_eq, neg_eq_zero]
rintroThe `rintro` tactic is a combination of the `intros` tactic with `rcases` to
allow for destructuring patterns while introducing variables. See `rcases` for
a description of supported patterns. For example, `rintro (a | ⟨b, c⟩) ⟨d, e⟩`
will introduce two variables, and then do case splits on both of them producing
two subgoals, one with variables `a d e` and the other with `b c d e`.
`rintro`, unlike `rcases`, also supports the form `(x y : ty)` for introducing
and type-ascripting multiple variables at once, similar to binders.
rfl
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
: δ < (0 : ℂ).re := hw.1
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[Complex.zero_re] atLocation specifications are used by many tactics that can operate on either the
hypotheses or the goal. It can have one of the forms:
* 'empty' is not actually present in this syntax, but most tactics use
`(location)?` matchers. It means to target the goal only.
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
* `at *`: target all hypotheses and the goal
this
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[Complex.zero_cpow hw0]
· haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hne : ((n : ℕ) : ℂ) ≠ 0 := Nat.cast_ne_zero.2 hn.ne'
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hne1 : ((n : ℕ) : ℂ) + 1 ≠ 0 := Nat.cast_add_one_ne_zero n
exact`exact e` closes the main goal if its target type matches that of `e`.
(((differentiable_neg.const_cpow (Or.inl hne)).sub
(differentiable_neg.const_cpow (Or.inl hne1))).const_mul _).differentiableOn
Real-segment bound. Suppose the partial sums of f are bounded by C and vanish
for n < n_0 (with n_0 \ge 1). Then for real \sigma \ge 0,
\bigl\| \tilde A_f(\sigma) \bigr\| \;\le\; C\, n_0^{-\sigma}.
Lean code for Lemma norm_bpSeries_le
-
⟲
theorem norm_bpSeries_le (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) {n₀ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn₀1 ≤ n₀: 1ℕ≤ n₀ℕ) (hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0: ∀ nℕ< n₀ℕ, ∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ= 0ℂ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ0 ≤ σ: 0ℝ≤ σℝ) : ‖bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂσℝ‖ ≤ Cℝ* (n₀ℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) ^ (-σℝ)theorem norm_bpSeries_le (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) {n₀ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn₀1 ≤ n₀: 1ℕ≤ n₀ℕ) (hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0: ∀ nℕ< n₀ℕ, ∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ= 0ℂ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ0 ≤ σ: 0ℝ≤ σℝ) : ‖bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂσℝ‖ ≤ Cℝ* (n₀ℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) ^ (-σℝ)
The partial sums of \sum_n \|\text{term}\| are bounded by C\, n_0^{-\sigma} (previous
lemma), so the full series \|\tilde A_f(\sigma)\| \le \sum_n \|\text{term}\| inherits that
bound. In the race application the coefficients are supported on primes, so the partial sums
below n = 2 vanish, giving n_0 = 2 and the bound C \cdot 2^{-\sigma}. \blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hnormsumSummable fun n => ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-↑σ) - (↑n + 1) ^ (-↑σ))‖ : SummableSummable.{u_1, u_2} {α : Type u_1} {β : Type u_2} [AddCommMonoid α] [TopologicalSpace α] (f : β → α)
(L : SummationFilter β := SummationFilter.unconditional β) : Prop`Summable f` means that `f` has some (infinite) sum with respect to `L`. Use `tsum` to get the
value. fun nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
↦
‖(∑ kℕ ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`. (nℕ + 1ℕ), fℕ → ℂ kℕ) *
((nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-(σℝ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )) - ((nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) + 1ℂ) ^ (-(σℝ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )))‖ :=
summable_of_sum_range_lesummable_of_sum_range_le {f : ℕ → ℝ} {c : ℝ} (hf : ∀ (n : ℕ), 0 ≤ f n) (h : ∀ (n : ℕ), ∑ i ∈ Finset.range n, f i ≤ c) :
Summable f (fun nℕ ↦ norm_nonnegnorm_nonneg.{u_5} {E : Type u_5} [SeminormedAddGroup E] (a : E) : 0 ≤ ‖a‖ _)
(fun Nℕ ↦ sum_range_norm_bpSeries_lesum_range_norm_bpSeries_le {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {n₀ : ℕ}
(hn₀ : 1 ≤ n₀) (hvanish : ∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0) {σ : ℝ} (hσ : 0 ≤ σ) (N : ℕ) :
∑ n ∈ Finset.range N, ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-↑σ) - (↑n + 1) ^ (-↑σ))‖ ≤ C * ↑n₀ ^ (-σ)**Telescoping majorant**: if the partial sums of `f` are bounded by `C` and vanish below
`n₀ ≥ 1`, then every partial sum of `∑ ‖by-parts term‖` on the real axis is at most
`C · n₀ ^ (-σ)`. The nonnegative real increments telescope from `n₀` (the earlier terms
vanish), so the internal majorant `n ↦ (max n n₀) ^ (-σ)` collapses to `n₀ ^ (-σ)`. hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C hn₀1 ≤ n₀ hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0 hσ0 ≤ σ Nℕ)
calcStep-wise reasoning over transitive relations.
```
calc
a = b := pab
b = c := pbc
...
y = z := pyz
```
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
relation implementing the typeclass `Trans`. Instead of repeating the right-
hand sides, subsequent left-hand sides can be replaced with `_`.
```
calc
a = b := pab
_ = c := pbc
...
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc
_ = bce := pabce
_ = cef := pbcef
...
_ = xyz := pwxyz
```
`calc` works as a term, as a tactic or as a `conv` tactic.
See [Theorem Proving in Lean 4][tpil4] for more information.
[tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
‖bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`:
`bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`.
If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this
series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it
agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re`
(`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series
to the right half-plane. This is the analytic-continuation object `Ã` of the proof. fℕ → ℂ σℝ‖
≤ ∑' nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
, ‖(∑ kℕ ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`. (nℕ + 1ℕ), fℕ → ℂ kℕ) *
((nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-(σℝ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )) - ((nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) + 1ℂ) ^ (-(σℝ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. )))‖ :=
norm_tsum_le_tsum_normnorm_tsum_le_tsum_norm.{u_1, u_3} {ι : Type u_1} {E : Type u_3} [SeminormedAddCommGroup E] {f : ι → E}
(hf : Summable fun i => ‖f i‖) : ‖∑' (i : ι), f i‖ ≤ ∑' (i : ι), ‖f i‖If `∑' i, ‖f i‖` is summable, then `‖∑' i, f i‖ ≤ (∑' i, ‖f i‖)`. Note that we do not assume
that `∑' i, f i` is summable, and it might not be the case if `α` is not a complete space. hnormsumSummable fun n => ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-↑σ) - (↑n + 1) ^ (-↑σ))‖
_ ≤ Cℝ * (n₀ℕ : ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. ) ^ (-σℝ) :=
Real.tsum_le_of_sum_range_leReal.tsum_le_of_sum_range_le {f : ℕ → ℝ} {c : ℝ} (hf : ∀ (n : ℕ), 0 ≤ f n)
(h : ∀ (n : ℕ), ∑ i ∈ Finset.range n, f i ≤ c) : ∑' (n : ℕ), f n ≤ c (fun nℕ ↦ norm_nonnegnorm_nonneg.{u_5} {E : Type u_5} [SeminormedAddGroup E] (a : E) : 0 ≤ ‖a‖ _)
(fun Nℕ ↦ sum_range_norm_bpSeries_lesum_range_norm_bpSeries_le {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {n₀ : ℕ}
(hn₀ : 1 ≤ n₀) (hvanish : ∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0) {σ : ℝ} (hσ : 0 ≤ σ) (N : ℕ) :
∑ n ∈ Finset.range N, ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-↑σ) - (↑n + 1) ^ (-↑σ))‖ ≤ C * ↑n₀ ^ (-σ)**Telescoping majorant**: if the partial sums of `f` are bounded by `C` and vanish below
`n₀ ≥ 1`, then every partial sum of `∑ ‖by-parts term‖` on the real axis is at most
`C · n₀ ^ (-σ)`. The nonnegative real increments telescope from `n₀` (the earlier terms
vanish), so the internal majorant `n ↦ (max n n₀) ^ (-σ)` collapses to `n₀ ^ (-σ)`. hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C hn₀1 ≤ n₀ hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0 hσ0 ≤ σ Nℕ)
Uniform term bound. If the partial sums of f are bounded by C, then on the box
\{\mathrm{Re}\, s > \delta\} \cap B(0, R) (with R \ge 0, \delta > 0) the n-th
by-parts term has norm at most the majorant of the previous lemma: C for n = 0 and
C\,R\, n^{-\delta - 1} for n \ge 1.
Lean code for Lemma norm_bpSeries_term_le_boxBound
-
⌁
theorem norm_bpSeries_term_le_boxBound
norm_bpSeries_term_le_boxBound {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {R δ : ℝ} (hR0 : 0 ≤ R) (hδ : 0 < δ) (n : ℕ) (w : ℂ) (hw : w ∈ {s | δ < s.re} ∩ Metric.ball 0 R) : ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-w) - (↑n + 1) ^ (-w))‖ ≤ if n = 0 then C else C * R * ↑n ^ (-δ - 1)**Uniform `M`-test bound on the box**: if the partial sums of `f` are bounded by `C`, then on `{s | δ < s.re} ∩ ball 0 R` each by-parts term satisfies `‖term n w‖ ≤ if n = 0 then C else C · R · n ^ (-δ - 1)`.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Cℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.), ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ) {Rℝδℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hR00 ≤ R: 0 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Rℝ) (hδ0 < δ: 0 <LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.δℝ) (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) (wℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) (hww ∈ {s | δ < s.re} ∩ Metric.ball 0 R: wℂ∈Membership.mem.{u, v} {α : outParam (Type u)} {γ : Type v} [self : Membership α γ] : γ → α → PropThe membership relation `a ∈ s : Prop` where `a : α`, `s : γ`. Conventions for notations in identifiers: * The recommended spelling of `∈` in identifiers is `mem`.{setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`sℂ|setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`δℝ<LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.}setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`∩Inter.inter.{u} {α : Type u} [self : Inter α] : α → α → α`a ∩ b` is the intersection of `a` and `b`. Conventions for notations in identifiers: * The recommended spelling of `∩` in identifiers is `inter`.Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε`0 Rℝ) : ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.(∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).wℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).wℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.ifite.{u} {α : Sort u} (c : Prop) [h : Decidable c] (t e : α) : α`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.nℕ=Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, `Eq.refl`. We use `a = b` as notation for `Eq a b`. A fundamental property of equality is that it is an equivalence relation. ``` variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd ``` Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given `h1 : a = b` and `h2 : p a`, we can construct a proof for `p b` using substitution: `Eq.subst h1 h2`. Example: ``` example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2 ``` The triangle in the second presentation is a macro built on top of `Eq.subst` and `Eq.symm`, and you can enter it by typing `\t`. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) Conventions for notations in identifiers: * The recommended spelling of `=` in identifiers is `eq`.0 thenite.{u} {α : Sort u} (c : Prop) [h : Decidable c] (t e : α) : α`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.Cℝelseite.{u} {α : Sort u} (c : Prop) [h : Decidable c] (t e : α) : α`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.Cℝ*HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.Rℝ*HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).δℝ-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).1)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).theorem norm_bpSeries_term_le_boxBound
norm_bpSeries_term_le_boxBound {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {R δ : ℝ} (hR0 : 0 ≤ R) (hδ : 0 < δ) (n : ℕ) (w : ℂ) (hw : w ∈ {s | δ < s.re} ∩ Metric.ball 0 R) : ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-w) - (↑n + 1) ^ (-w))‖ ≤ if n = 0 then C else C * R * ↑n ^ (-δ - 1)**Uniform `M`-test bound on the box**: if the partial sums of `f` are bounded by `C`, then on `{s | δ < s.re} ∩ ball 0 R` each by-parts term satisfies `‖term n w‖ ≤ if n = 0 then C else C · R · n ^ (-δ - 1)`.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Cℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.), ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ) {Rℝδℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hR00 ≤ R: 0 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Rℝ) (hδ0 < δ: 0 <LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.δℝ) (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) (wℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) (hww ∈ {s | δ < s.re} ∩ Metric.ball 0 R: wℂ∈Membership.mem.{u, v} {α : outParam (Type u)} {γ : Type v} [self : Membership α γ] : γ → α → PropThe membership relation `a ∈ s : Prop` where `a : α`, `s : γ`. Conventions for notations in identifiers: * The recommended spelling of `∈` in identifiers is `mem`.{setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`sℂ|setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`δℝ<LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: `x < y` Conventions for notations in identifiers: * The recommended spelling of `<` in identifiers is `lt`.sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.}setOf.{u} {α : Type u} (p : α → Prop) : Set αTurn a predicate `p : α → Prop` into a set, also written as `{x | p x}`∩Inter.inter.{u} {α : Type u} [self : Inter α] : α → α → α`a ∩ b` is the intersection of `a` and `b`. Conventions for notations in identifiers: * The recommended spelling of `∩` in identifiers is `inter`.Metric.ballMetric.ball.{u} {α : Type u} [PseudoMetricSpace α] (x : α) (ε : ℝ) : Set α`ball x ε` is the set of all points `y` with `dist y x < ε`0 Rℝ) : ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.(∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).wℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).wℂ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.ifite.{u} {α : Sort u} (c : Prop) [h : Decidable c] (t e : α) : α`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.nℕ=Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, `Eq.refl`. We use `a = b` as notation for `Eq a b`. A fundamental property of equality is that it is an equivalence relation. ``` variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd ``` Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given `h1 : a = b` and `h2 : p a`, we can construct a proof for `p b` using substitution: `Eq.subst h1 h2`. Example: ``` example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2 ``` The triangle in the second presentation is a macro built on top of `Eq.subst` and `Eq.symm`, and you can enter it by typing `\t`. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) Conventions for notations in identifiers: * The recommended spelling of `=` in identifiers is `eq`.0 thenite.{u} {α : Sort u} (c : Prop) [h : Decidable c] (t e : α) : α`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.Cℝelseite.{u} {α : Sort u} (c : Prop) [h : Decidable c] (t e : α) : α`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false.Cℝ*HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.Rℝ*HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).δℝ-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).1)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).
Bound the coefficient by C and the increment \|n^{-s} - (n+1)^{-s}\| by the increment
bound \|s\|\, n^{-\mathrm{Re}\,s - 1}, then use \|s\| < R and
n^{-\mathrm{Re}\,s - 1} \le n^{-\delta - 1} on the box. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hC0 : 0 ≤ C := (norm_nonneg _).trans (hC 0)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hwre : δ < w.re := hw.1
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hwnorm : ‖w‖ < R := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
:= hw.2
rwa`rwa` is short-hand for `rw; assumption`. [Metric.mem_ball, dist_zero_right] atLocation specifications are used by many tactics that can operate on either the
hypotheses or the goal. It can have one of the forms:
* 'empty' is not actually present in this syntax, but most tactics use
`(location)?` matchers. It means to target the goal only.
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
* `at *`: target all hypotheses and the goal
this
rcases`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
Nat.eq_zero_or_pos n with`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
rfl | hn
· rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[if_pos rfl]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hw0 : -w ≠ 0 := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[ne_eq, neg_eq_zero]
rintroThe `rintro` tactic is a combination of the `intros` tactic with `rcases` to
allow for destructuring patterns while introducing variables. See `rcases` for
a description of supported patterns. For example, `rintro (a | ⟨b, c⟩) ⟨d, e⟩`
will introduce two variables, and then do case splits on both of them producing
two subgoals, one with variables `a d e` and the other with `b c d e`.
`rintro`, unlike `rcases`, also supports the form `(x y : ty)` for introducing
and type-ascripting multiple variables at once, similar to binders.
rfl
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[Complex.zero_re] atLocation specifications are used by many tactics that can operate on either the
hypotheses or the goal. It can have one of the forms:
* 'empty' is not actually present in this syntax, but most tactics use
`(location)?` matchers. It means to target the goal only.
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
* `at *`: target all hypotheses and the goal
hwre
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
simpaThis is a "finishing" tactic modification of `simp`. It has two forms.
* `simpa [rules, ⋯] using e` will simplify the goal and the type of
`e` using `rules`, then try to close the goal using `e`.
Simplifying the type of `e` makes it more likely to match the goal
(which has also been simplified). This construction also tends to be
more robust under changes to the simp lemma set.
The final match between the simplified `e` and the simplified goal uses
**reducible** transparency, so it does not unfold semireducible definitions.
Write `simpa [rules, ⋯] using! e` to perform the match at the ambient
(default/semireducible) transparency instead.
* `simpa [rules, ⋯]` will simplify the goal and the type of a
hypothesis `this` if present in the context, then try to close the goal using
the `assumption` tactic.
As with `simp`, the `!` modifier after `simpa` enables auto-unfolding of
definitions in the simp set.
[Complex.zero_cpow hw0] usingThe arguments to the `simpa` family tactics. hC 0
· rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[if_neg hn.ne']
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h1n : (1 : ℝ) ≤ (n : ℝ) := Nat.one_le_cast.2 hn
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[norm_mul]
calcStep-wise reasoning over transitive relations.
```
calc
a = b := pab
b = c := pbc
...
y = z := pyz
```
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
relation implementing the typeclass `Trans`. Instead of repeating the right-
hand sides, subsequent left-hand sides can be replaced with `_`.
```
calc
a = b := pab
_ = c := pbc
...
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc
_ = bce := pabce
_ = cef := pbcef
...
_ = xyz := pwxyz
```
`calc` works as a term, as a tactic or as a `conv` tactic.
See [Theorem Proving in Lean 4][tpil4] for more information.
[tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
‖∑ k ∈ Finset.range (n + 1), f k‖ * ‖(n : ℂ) ^ (-w) - ((n : ℂ) + 1) ^ (-w)‖
≤ C * (‖w‖ * (n : ℝ) ^ (-w.re - 1)) :=
mul_le_mul (hC n)
(Complex.norm_natCast_cpow_sub_add_one_cpow_le (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
) hn)
(norm_nonneg _) hC0
_ ≤ C * (R * (n : ℝ) ^ (-δ - 1)) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
mul_le_mul_of_nonneg_left ?_ hC0
exact`exact e` closes the main goal if its target type matches that of `e`.
mul_le_mul hwnorm.le
(Real.rpow_le_rpow_of_exponent_le h1n (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
))
(Real.rpow_nonneg (Nat.cast_nonneg n) _) hR0
_ = C * R * (n : ℝ) ^ (-δ - 1) := (mul_assoc _ _ _).symm
Theorem. The prime race constant \varrho = \sum_{k \ge 0} b_k\,2^{-(k+1)} — the number
whose binary expansion is 0.b_0 b_1 b_2 \ldots_2, where b_k = 1 exactly when the
k-th odd prime is \equiv 3 \pmod 4 — is irrational.
Lean code for Theorem irrational_ϱ
-
⟲
theorem irrational_ϱ : Irrational
Irrational (x : ℝ) : PropA real number is irrational if it is not equal to any rational number.ϱA362583.ϱ : ℝThe prime race constant (the A362583 constant): the sum of the series `ϱ = Σ_{k ≥ 0} bit k · 2^{-(k+1)}`, i.e. the real number whose `k`-th binary digit is `bit k` — in binary `0.b₀b₁b₂…₂ ≈ 0.7004001…` (decimal). OEIS A362583 lists the successive digit prefixes read as binary integers.theorem irrational_ϱ : Irrational
Irrational (x : ℝ) : PropA real number is irrational if it is not equal to any rational number.ϱA362583.ϱ : ℝThe prime race constant (the A362583 constant): the sum of the series `ϱ = Σ_{k ≥ 0} bit k · 2^{-(k+1)}`, i.e. the real number whose `k`-th binary digit is `bit k` — in binary `0.b₀b₁b₂…₂ ≈ 0.7004001…` (decimal). OEIS A362583 lists the successive digit prefixes read as binary integers.
Pure logic; no new mathematics. If \varrho were rational, its bit sequence would be
eventually periodic; eventual periodicity would force the race sum onto a linear trajectory
|S(N) - c\,\pi(N)| \le C; and the main analytic theorem says no such trajectory exists.
\blacksquare
⟲by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
by_contra`by_contra h` proves `⊢ p` by contradiction,
introducing a hypothesis `h : ¬p` and proving `False`.
* If `p` is a negation `¬q`, `h : q` will be introduced instead of `¬¬q`.
* If `p` is decidable, it uses `Decidable.byContradiction` instead of `Classical.byContradiction`.
* If `h` is omitted, the introduced variable will be called `this`.
* `h` can be any pattern supported by `rcases`/`rintro`.
h¬Irrational ϱ
exact`exact e` closes the main goal if its target type matches that of `e`.
raceSum_not_linearA362583.raceSum_not_linear : ¬∃ c C, ∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ C**Main analytic theorem**: the mod-4 prime race is never linear — there are no
constants `c`, `C` with `|S(N) - c·π(N)| ≤ C` for all `N`, where `S = raceSum` and
`π = Nat.primeCounting` (# primes `≤ N`).
Case `c ≠ 0` is `c_eq_zero_of_raceSum_linear`; case `c = 0` reduces to the bounded-race
hypothesis `|S(N)| ≤ C`. The continued logarithm `contLog` satisfies
`exp ∘ contLog = L(χ, ·)` on `Ω = {Re s > 1/2}` by the identity theorem
(`norm_LFunction_eq_exp_re_contLog`), but `Re (contLog σ) ≥ layerBReal σ - C - cT → ∞` as
`σ ↓ 1/2` by divergence transfer (`layerBReal_sub_le_re_contLog` + `exists_layerBReal_gt`),
contradicting the continuity of the entire `L(χ, ·)` at `1/2`
(`exists_norm_LFunction_lt_near_half`) — evaluated at a single point `σ*`, with no filters.
(raceSum_linear_of_eventuallyPeriodicA362583.raceSum_linear_of_eventuallyPeriodic :
(∃ N P, 0 < P ∧ ∀ k ≥ N, bit (k + P) = bit k) → ∃ c C, ∀ (N : ℕ), |↑(raceSum N) - c * ↑N.primeCounting| ≤ CAn eventually periodic bit sequence makes
the race sum linear in the prime count, `raceSum N = c·π(N) + O(1)`. The slope
is `c = W/P` for the exact window sum `W = ∑_{k∈[N₀,N₀+P)} (1 - 2·bit k) = P - 2j`,
and the constant `C = 2·N₀ + 2·P + 1` absorbs the `m = π(N) - 1` reindexing
via `|c| ≤ 1` (all-ones/all-zeros patterns are just `c = ∓1`, no separate case). (eventuallyPeriodic_of_not_irrationalA362583.eventuallyPeriodic_of_not_irrational : ¬Irrational ϱ → ∃ N P, 0 < P ∧ ∀ k ≥ N, bit (k + P) = bit kIf `ϱ` is rational, its bit sequence is
eventually periodic — pigeonhole two equal tails `t m = t n` (`exists_t_collision`),
propagate the collision by the recurrence's determinism (`t_eq_add`), giving period
`P = n - m` from index `m`. h¬Irrational ϱ))
Telescoping partial sums. Under the bounded-partial-sum hypothesis, if the partial sums of
f vanish for n < n_0 (with n_0 \ge 1), then for real \sigma \ge 0 and every N
\sum_{n < N} \Bigl\| \Bigl(\sum_{k \le n} f(k)\Bigr)\bigl(n^{-\sigma} - (n+1)^{-\sigma}\bigr) \Bigr\|
\;\le\; C\, n_0^{-\sigma}.
Lean code for Lemma sum_range_norm_bpSeries_le
-
⌁
theorem sum_range_norm_bpSeries_le
sum_range_norm_bpSeries_le {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {n₀ : ℕ} (hn₀ : 1 ≤ n₀) (hvanish : ∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0) {σ : ℝ} (hσ : 0 ≤ σ) (N : ℕ) : ∑ n ∈ Finset.range N, ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-↑σ) - (↑n + 1) ^ (-↑σ))‖ ≤ C * ↑n₀ ^ (-σ)**Telescoping majorant**: if the partial sums of `f` are bounded by `C` and vanish below `n₀ ≥ 1`, then every partial sum of `∑ ‖by-parts term‖` on the real axis is at most `C · n₀ ^ (-σ)`. The nonnegative real increments telescope from `n₀` (the earlier terms vanish), so the internal majorant `n ↦ (max n n₀) ^ (-σ)` collapses to `n₀ ^ (-σ)`.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Cℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.), ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ) {n₀ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn₀1 ≤ n₀: 1 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.n₀ℕ) (hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0: ∀ nℕ< n₀ℕ, ∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ=Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, `Eq.refl`. We use `a = b` as notation for `Eq a b`. A fundamental property of equality is that it is an equivalence relation. ``` variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd ``` Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given `h1 : a = b` and `h2 : p a`, we can construct a proof for `p b` using substitution: `Eq.subst h1 h2`. Example: ``` example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2 ``` The triangle in the second presentation is a macro built on top of `Eq.subst` and `Eq.symm`, and you can enter it by typing `\t`. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) Conventions for notations in identifiers: * The recommended spelling of `=` in identifiers is `eq`.0) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ0 ≤ σ: 0 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.σℝ) (Nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ∑ nℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.Nℕ, ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.(∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).↑σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).↑σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ*HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.↑n₀ℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).theorem sum_range_norm_bpSeries_le
sum_range_norm_bpSeries_le {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {n₀ : ℕ} (hn₀ : 1 ≤ n₀) (hvanish : ∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0) {σ : ℝ} (hσ : 0 ≤ σ) (N : ℕ) : ∑ n ∈ Finset.range N, ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-↑σ) - (↑n + 1) ^ (-↑σ))‖ ≤ C * ↑n₀ ^ (-σ)**Telescoping majorant**: if the partial sums of `f` are bounded by `C` and vanish below `n₀ ≥ 1`, then every partial sum of `∑ ‖by-parts term‖` on the real axis is at most `C · n₀ ^ (-σ)`. The nonnegative real increments telescope from `n₀` (the earlier terms vanish), so the internal majorant `n ↦ (max n n₀) ^ (-σ)` collapses to `n₀ ^ (-σ)`.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Cℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.), ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ) {n₀ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn₀1 ≤ n₀: 1 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.n₀ℕ) (hvanish∀ n < n₀, ∑ k ∈ Finset.range (n + 1), f k = 0: ∀ nℕ< n₀ℕ, ∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ=Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, `Eq.refl`. We use `a = b` as notation for `Eq a b`. A fundamental property of equality is that it is an equivalence relation. ``` variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd ``` Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given `h1 : a = b` and `h2 : p a`, we can construct a proof for `p b` using substitution: `Eq.subst h1 h2`. Example: ``` example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2 ``` The triangle in the second presentation is a macro built on top of `Eq.subst` and `Eq.symm`, and you can enter it by typing `\t`. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) Conventions for notations in identifiers: * The recommended spelling of `=` in identifiers is `eq`.0) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ0 ≤ σ: 0 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.σℝ) (Nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ∑ nℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.Nℕ, ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.(∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).↑σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).↑σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ*HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.↑n₀ℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).
The terms below n_0 vanish; for n \ge n_0 apply the real-axis term bound and telescope,
\sum_{n < N} C\,(n^{-\sigma} - (n+1)^{-\sigma}) = C\,(n_0^{-\sigma} - N^{-\sigma}) \le C\, n_0^{-\sigma}.
\blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hC0 : 0 ≤ C := (norm_nonneg _).trans (hC 0)
set`set a := t with h` is a variant of `let a := t`. It adds the hypothesis `h : a = t` to
the local context and replaces `t` with `a` everywhere it can.
`set a := t with ← h` will add `h : t = a` instead.
`set! a := t with h` does not do any replacing.
```lean
example (x : Nat) (h : x + x - x = 3) : x + x - x = 3 := by
set y := x with ← h2
sorry
/-
x : Nat
y : Nat := x
h : y + y - y = 3
h2 : x = y
⊢ y + y - y = 3
-/
```
G : ℕ → ℝ := fun n ↦ ((max n n₀ : ℕ) : ℝ) ^ (-σ) with hG
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hterm : ∀ n : ℕ,
‖(∑ k ∈ Finset.range (n + 1), f k) *
((n : ℂ) ^ (-(σ : ℂ)) - ((n : ℂ) + 1) ^ (-(σ : ℂ)))‖ ≤ C * (G n - G (n + 1)) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
introIntroduces one or more hypotheses, optionally naming and/or pattern-matching them.
For each hypothesis to be introduced, the remaining main goal's target type must
be a `let` or function type.
* `intro` by itself introduces one anonymous hypothesis, which can be accessed
by e.g. `assumption`. It is equivalent to `intro _`.
* `intro x y` introduces two hypotheses and names them. Individual hypotheses
can be anonymized via `_`, given a type ascription, or matched against a pattern:
```lean
-- ... ⊢ α × β → ...
intro (a, b)
-- ..., a : α, b : β ⊢ ...
```
* `intro rfl` is short for `intro h; subst h`, if `h` is an equality where the left-hand or right-hand side
is a variable.
* Alternatively, `intro` can be combined with pattern matching much like `fun`:
```lean
intro
| n + 1, 0 => tac
| ...
```
n
rcases`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
lt_or_ge n n₀ with`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
hlt | hle
· rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hvanish n hlt, zero_mul, norm_zero]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h1 : max n n₀ = n₀ := max_eq_right hlt.le
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h2 : max (n + 1) n₀ = n₀ := max_eq_right (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. omegaThe `omega` tactic, for resolving integer and natural linear arithmetic problems.
It is not yet a full decision procedure (no "dark" or "grey" shadows),
but should be effective on many problems.
We handle hypotheses of the form `x = y`, `x < y`, `x ≤ y`, and `k ∣ x` for `x y` in `Nat` or `Int`
(and `k` a literal), along with negations of these statements.
We decompose the sides of the inequalities as linear combinations of atoms.
If we encounter `x / k` or `x % k` for literal integers `k` we introduce new auxiliary variables
and the relevant inequalities.
On the first pass, we do not perform case splits on natural subtraction.
If `omega` fails, we recursively perform a case split on
a natural subtraction appearing in a hypothesis, and try again.
The options
```
omega +splitDisjunctions +splitNatSub +splitNatAbs +splitMinMax
```
can be used to:
* `splitDisjunctions`: split any disjunctions found in the context,
if the problem is not otherwise solvable.
* `splitNatSub`: for each appearance of `((a - b : Nat) : Int)`, split on `a ≤ b` if necessary.
* `splitNatAbs`: for each appearance of `Int.natAbs a`, split on `0 ≤ a` if necessary.
* `splitMinMax`: for each occurrence of `min a b`, split on `min a b = a ∨ min a b = b`
Currently, all of these are on by default.
)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hzero : G n - G (n + 1) = 0 := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hG]
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[h1, h2, sub_self]
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[hzero]
· haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hn1 : 1 ≤ n := hn₀.trans hle
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hGn : G n = (n : ℝ) ^ (-σ) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hG]
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[max_eq_left hle]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hGn1 : G (n + 1) = ((n : ℝ) + 1) ^ (-σ) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hG]
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[max_eq_left (show n₀ ≤ n + 1 by omegaThe `omega` tactic, for resolving integer and natural linear arithmetic problems.
It is not yet a full decision procedure (no "dark" or "grey" shadows),
but should be effective on many problems.
We handle hypotheses of the form `x = y`, `x < y`, `x ≤ y`, and `k ∣ x` for `x y` in `Nat` or `Int`
(and `k` a literal), along with negations of these statements.
We decompose the sides of the inequalities as linear combinations of atoms.
If we encounter `x / k` or `x % k` for literal integers `k` we introduce new auxiliary variables
and the relevant inequalities.
On the first pass, we do not perform case splits on natural subtraction.
If `omega` fails, we recursively perform a case split on
a natural subtraction appearing in a hypothesis, and try again.
The options
```
omega +splitDisjunctions +splitNatSub +splitNatAbs +splitMinMax
```
can be used to:
* `splitDisjunctions`: split any disjunctions found in the context,
if the problem is not otherwise solvable.
* `splitNatSub`: for each appearance of `((a - b : Nat) : Int)`, split on `a ≤ b` if necessary.
* `splitNatAbs`: for each appearance of `Int.natAbs a`, split on `0 ≤ a` if necessary.
* `splitMinMax`: for each occurrence of `min a b`, split on `min a b = a ∨ min a b = b`
Currently, all of these are on by default.
), Nat.cast_add, Nat.cast_one]
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hGn, hGn1]
exact`exact e` closes the main goal if its target type matches that of `e`.
norm_bpSeries_term_le hC hσ hn1
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hG0 : G 0 = (n₀ : ℝ) ^ (-σ) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hG]
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[Nat.zero_max]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hGN : 0 ≤ G N := Real.rpow_nonneg (Nat.cast_nonneg _) _
calcStep-wise reasoning over transitive relations.
```
calc
a = b := pab
b = c := pbc
...
y = z := pyz
```
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
relation implementing the typeclass `Trans`. Instead of repeating the right-
hand sides, subsequent left-hand sides can be replaced with `_`.
```
calc
a = b := pab
_ = c := pbc
...
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc
_ = bce := pabce
_ = cef := pbcef
...
_ = xyz := pwxyz
```
`calc` works as a term, as a tactic or as a `conv` tactic.
See [Theorem Proving in Lean 4][tpil4] for more information.
[tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
∑ n ∈ Finset.range N,
‖(∑ k ∈ Finset.range (n + 1), f k) *
((n : ℂ) ^ (-(σ : ℂ)) - ((n : ℂ) + 1) ^ (-(σ : ℂ)))‖
≤ ∑ n ∈ Finset.range N, C * (G n - G (n + 1)) :=
Finset.sum_le_sum fun n _ ↦ hterm n
_ = C * (G 0 - G N) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[← Finset.mul_sum, Finset.sum_range_sub' G N]
_ ≤ C * (n₀ : ℝ) ^ (-σ) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[← hG0]
exact`exact e` closes the main goal if its target type matches that of `e`.
mul_le_mul_of_nonneg_left (sub_le_self _ hGN) hC0
If \varrho is not irrational, then the bit sequence is eventually periodic: there
exist N and P > 0 with b_{k+P} = b_k for all k \ge N.
Lean code for Theorem eventuallyPeriodic_of_not_irrational
-
≈
theorem eventuallyPeriodic_of_not_irrational : ¬ Irrational
Irrational (x : ℝ) : PropA real number is irrational if it is not equal to any rational number.ϱA362583.ϱ : ℝThe prime race constant (the A362583 constant): the sum of the series `ϱ = Σ_{k ≥ 0} bit k · 2^{-(k+1)}`, i.e. the real number whose `k`-th binary digit is `bit k` — in binary `0.b₀b₁b₂…₂ ≈ 0.7004001…` (decimal). OEIS A362583 lists the successive digit prefixes read as binary integers.→ ∃ NℕPℕ, 0ℕ< Pℕ∧ ∀ kℕ≥ Nℕ, bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).(kℕ+ Pℕ) = bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).kℕtheorem eventuallyPeriodic_of_not_irrational : ¬ Irrational
Irrational (x : ℝ) : PropA real number is irrational if it is not equal to any rational number.ϱA362583.ϱ : ℝThe prime race constant (the A362583 constant): the sum of the series `ϱ = Σ_{k ≥ 0} bit k · 2^{-(k+1)}`, i.e. the real number whose `k`-th binary digit is `bit k` — in binary `0.b₀b₁b₂…₂ ≈ 0.7004001…` (decimal). OEIS A362583 lists the successive digit prefixes read as binary integers.→ ∃ NℕPℕ, 0ℕ< Pℕ∧ ∀ kℕ≥ Nℕ, bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).(kℕ+ Pℕ) = bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).kℕ
Define the binary tails t_k := \sum_{j \ge 0} b_{k+j}\, 2^{-(j+1)}, so that t_0 = \varrho;
the argument runs through the tails, in the Lean file's zero-based indexing.
First, t_k \in (0,1) strictly for every k: infinitely many later ones force
t_k > 0, and infinitely many later zeros force t_k < 1. Next, the recurrence
t_k = (b_k + t_{k+1})/2 holds by splitting off the first term of the tail, so if b_k = 1
then t_k \in (1/2, 1) and if b_k = 0 then t_k \in (0, 1/2). In particular
t_k \ne 1/2 always, so t_k determines b_k (b_k = 1 \iff t_k > 1/2) and hence
also t_{k+1} = 2 t_k - b_k.
Because 2^k \varrho is an integer plus t_k and t_k \in (0,1), we have
t_k = \operatorname{fract}(2^k \varrho). If \varrho is rational with denominator b,
then each t_k = \operatorname{fract}(2^k \varrho) lies in the finite set
\{0, 1/b, \ldots, (b-1)/b\} — in Lean, the denominator of the rational witness produced by
unfolding "not irrational" — so by pigeonhole there are m < n with t_m = t_n. Since
t_k determines both b_k and t_{k+1}, induction propagates the collision:
t_{m+i} = t_{n+i} and b_{m+i} = b_{n+i} for all i \ge 0. Hence the bits are periodic
with period P = n - m from index m. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
introIntroduces one or more hypotheses, optionally naming and/or pattern-matching them.
For each hypothesis to be introduced, the remaining main goal's target type must
be a `let` or function type.
* `intro` by itself introduces one anonymous hypothesis, which can be accessed
by e.g. `assumption`. It is equivalent to `intro _`.
* `intro x y` introduces two hypotheses and names them. Individual hypotheses
can be anonymized via `_`, given a type ascription, or matched against a pattern:
```lean
-- ... ⊢ α × β → ...
intro (a, b)
-- ..., a : α, b : β ⊢ ...
```
* `intro rfl` is short for `intro h; subst h`, if `h` is an equality where the left-hand or right-hand side
is a variable.
* Alternatively, `intro` can be combined with pattern matching much like `fun`:
```lean
intro
| n + 1, 0 => tac
| ...
```
h
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨m, n, hmn, ht⟩ := exists_t_collision h
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
⟨m, n - m, by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. omegaThe `omega` tactic, for resolving integer and natural linear arithmetic problems.
It is not yet a full decision procedure (no "dark" or "grey" shadows),
but should be effective on many problems.
We handle hypotheses of the form `x = y`, `x < y`, `x ≤ y`, and `k ∣ x` for `x y` in `Nat` or `Int`
(and `k` a literal), along with negations of these statements.
We decompose the sides of the inequalities as linear combinations of atoms.
If we encounter `x / k` or `x % k` for literal integers `k` we introduce new auxiliary variables
and the relevant inequalities.
On the first pass, we do not perform case splits on natural subtraction.
If `omega` fails, we recursively perform a case split on
a natural subtraction appearing in a hypothesis, and try again.
The options
```
omega +splitDisjunctions +splitNatSub +splitNatAbs +splitMinMax
```
can be used to:
* `splitDisjunctions`: split any disjunctions found in the context,
if the problem is not otherwise solvable.
* `splitNatSub`: for each appearance of `((a - b : Nat) : Int)`, split on `a ≤ b` if necessary.
* `splitNatAbs`: for each appearance of `Int.natAbs a`, split on `0 ≤ a` if necessary.
* `splitMinMax`: for each occurrence of `min a b`, split on `min a b = a ∨ min a b = b`
Currently, all of these are on by default.
, fun k hk ↦ ?_⟩
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨i, rfl⟩ : ∃ i, k = m + i := ⟨k - m, by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. omegaThe `omega` tactic, for resolving integer and natural linear arithmetic problems.
It is not yet a full decision procedure (no "dark" or "grey" shadows),
but should be effective on many problems.
We handle hypotheses of the form `x = y`, `x < y`, `x ≤ y`, and `k ∣ x` for `x y` in `Nat` or `Int`
(and `k` a literal), along with negations of these statements.
We decompose the sides of the inequalities as linear combinations of atoms.
If we encounter `x / k` or `x % k` for literal integers `k` we introduce new auxiliary variables
and the relevant inequalities.
On the first pass, we do not perform case splits on natural subtraction.
If `omega` fails, we recursively perform a case split on
a natural subtraction appearing in a hypothesis, and try again.
The options
```
omega +splitDisjunctions +splitNatSub +splitNatAbs +splitMinMax
```
can be used to:
* `splitDisjunctions`: split any disjunctions found in the context,
if the problem is not otherwise solvable.
* `splitNatSub`: for each appearance of `((a - b : Nat) : Int)`, split on `a ≤ b` if necessary.
* `splitNatAbs`: for each appearance of `Int.natAbs a`, split on `0 ≤ a` if necessary.
* `splitMinMax`: for each occurrence of `min a b`, split on `min a b = a ∨ min a b = b`
Currently, all of these are on by default.
⟩
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hbi : bit (m + i) = bit (n + i) := bit_eq_of_t_eq (t_eq_add ht i)
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[show m + i + (n - m) = n + i from by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. omegaThe `omega` tactic, for resolving integer and natural linear arithmetic problems.
It is not yet a full decision procedure (no "dark" or "grey" shadows),
but should be effective on many problems.
We handle hypotheses of the form `x = y`, `x < y`, `x ≤ y`, and `k ∣ x` for `x y` in `Nat` or `Int`
(and `k` a literal), along with negations of these statements.
We decompose the sides of the inequalities as linear combinations of atoms.
If we encounter `x / k` or `x % k` for literal integers `k` we introduce new auxiliary variables
and the relevant inequalities.
On the first pass, we do not perform case splits on natural subtraction.
If `omega` fails, we recursively perform a case split on
a natural subtraction appearing in a hypothesis, and try again.
The options
```
omega +splitDisjunctions +splitNatSub +splitNatAbs +splitMinMax
```
can be used to:
* `splitDisjunctions`: split any disjunctions found in the context,
if the problem is not otherwise solvable.
* `splitNatSub`: for each appearance of `((a - b : Nat) : Int)`, split on `a ≤ b` if necessary.
* `splitNatAbs`: for each appearance of `Int.natAbs a`, split on `0 ≤ a` if necessary.
* `splitMinMax`: for each occurrence of `min a b`, split on `min a b = a ∨ min a b = b`
Currently, all of these are on by default.
, ← hbi]
The k-th bit is
b_k = \begin{cases} 1 & \text{if } p_k \equiv 3 \pmod 4, \\ 0 & \text{if } p_k \equiv 1 \pmod 4. \end{cases}
Every odd prime is congruent to 1 or 3 mod 4, so the two cases are exhaustive. The
first eight bits are 1,0,1,1,0,0,1,1 (from the primes 3, 5, 7, 11, 13, 17, 19, 23).
Lean code for Definition bit
-
⟲
def bit (k
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.def bit (k
ℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.) : ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
⟲:= if`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
oddPrimeA362583.oddPrime (k : ℕ) : ℕThe `k`-th odd prime: `oddPrime 0 = 3`, `oddPrime 1 = 5`, `oddPrime 2 = 7`, ….
Writing the odd primes as `p_1, p_2, …`, this is `p_{k+1}`; since
`Nat.nth Nat.Prime 0 = 2`, skipping index 0 skips exactly the prime 2. kℕ % 4ℕ = 3ℕ then`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
1ℕ else`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
0ℕ
The set \{k \mid b_k = 1\} is infinite.
Lean code for Theorem bits_infinite_ones
-
≈
theorem bits_infinite_ones : {k
ℕ| bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).kℕ= 1ℕ}.InfiniteSet.Infinite.{u} {α : Type u} (s : Set α) : PropA set is infinite if it is not finite. This is protected so that it does not conflict with global `Infinite`.theorem bits_infinite_ones : {k
ℕ| bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).kℕ= 1ℕ}.InfiniteSet.Infinite.{u} {α : Type u} (s : Set α) : PropA set is infinite if it is not finite. This is protected so that it does not conflict with global `Infinite`.
By Dirichlet's theorem at modulus 4 — in Mathlib's form, the primes \equiv 3 \pmod 4
form an infinite set — infinitely many primes lie in the class 3 \bmod 4. These transfer to
bit indices along the injection sending a prime p to the number of primes below p, minus
one; its inverse is the odd-prime enumeration k \mapsto p_k, and 2 is excluded since
2 \not\equiv 3 \pmod 4. So infinitely many indices k have p_k \equiv 3 \pmod 4, i.e.
b_k = 1. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hidx := infinite_oddPrime_index infinite_primes_three_mod_four
(fun p hp ↦ hp.1) (fun h ↦ absurd h.2 (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. decide`decide` attempts to prove the main goal (with target type `p`) by synthesizing an instance of `Decidable p`
and then reducing that instance to evaluate the truth value of `p`.
If it reduces to `isTrue h`, then `h` is a proof of `p` that closes the goal.
The target is not allowed to contain local variables or metavariables.
If there are local variables, you can first try using the `revert` tactic with these local variables to move them into the target,
or you can use the `+revert` option, described below.
Options:
- `decide +revert` begins by reverting local variables that the target depends on,
after cleaning up the local context of irrelevant variables.
A variable is *relevant* if it appears in the target, if it appears in a relevant variable,
or if it is a proposition that refers to a relevant variable.
- `decide +kernel` uses kernel for reduction instead of the elaborator.
It has two key properties: (1) since it uses the kernel, it ignores transparency and can unfold everything,
and (2) it reduces the `Decidable` instance only once instead of twice.
- `decide +native` uses the native code compiler (`#eval`) to evaluate the `Decidable` instance,
admitting the result via an axiom. This can be significantly more efficient than using reduction, but it is at the cost of increasing the size
This can be significantly more efficient than using reduction, but it is at the cost of increasing the size
of the trusted code base.
Namely, it depends on the correctness of the Lean compiler and all definitions with an `@[implemented_by]` attribute.
Like with `+kernel`, the `Decidable` instance is evaluated only once.
Limitation: In the default mode or `+kernel` mode, since `decide` uses reduction to evaluate the term,
`Decidable` instances defined by well-founded recursion might not work because evaluating them requires reducing proofs.
Reduction can also get stuck on `Decidable` instances with `Eq.rec` terms.
These can appear in instances defined using tactics (such as `rw` and `simp`).
To avoid this, create such instances using definitions such as `decidable_of_iff` instead.
## Examples
Proving inequalities:
```lean
example : 2 + 2 ≠ 5 := by decide
```
Trying to prove a false proposition:
```lean
example : 1 ≠ 1 := by decide
/-
tactic 'decide' proved that the proposition
1 ≠ 1
is false
-/
```
Trying to prove a proposition whose `Decidable` instance fails to reduce
```lean
opaque unknownProp : Prop
open scoped Classical in
example : unknownProp := by decide
/-
tactic 'decide' failed for proposition
unknownProp
since its 'Decidable' instance reduced to
Classical.choice ⋯
rather than to the 'isTrue' constructor.
-/
```
## Properties and relations
For equality goals for types with decidable equality, usually `rfl` can be used in place of `decide`.
```lean
example : 1 + 1 = 2 := by decide
example : 1 + 1 = 2 := by rfl
```
))
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
hidx.mono fun k hk ↦ ?_
exact`exact e` closes the main goal if its target type matches that of `e`.
bit_eq_one_iff.mpr hk.2
Real-axis term bound. If the partial sums of f are bounded by C, then for real
\sigma \ge 0 and n \ge 1 the n-th by-parts term satisfies
\Bigl\| \Bigl(\sum_{k \le n} f(k)\Bigr)\bigl(n^{-\sigma} - (n+1)^{-\sigma}\bigr) \Bigr\|
\;\le\; C\,\bigl(n^{-\sigma} - (n+1)^{-\sigma}\bigr).
Lean code for Lemma norm_bpSeries_term_le
-
⌁
theorem norm_bpSeries_term_le
norm_bpSeries_term_le {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {σ : ℝ} (hσ : 0 ≤ σ) {n : ℕ} (hn : 1 ≤ n) : ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-↑σ) - (↑n + 1) ^ (-↑σ))‖ ≤ C * (↑n ^ (-σ) - (↑n + 1) ^ (-σ))**Per-term bound** for the real-axis by-parts series: for `1 ≤ n` and `0 ≤ σ`, the `n`-th term is at most `C` times the nonnegative increment `n ^ (-σ) - (n + 1) ^ (-σ)`.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Cℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.), ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ0 ≤ σ: 0 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.σℝ) {nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn1 ≤ n: 1 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.nℕ) : ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.(∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).↑σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).↑σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ*HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).theorem norm_bpSeries_term_le
norm_bpSeries_term_le {f : ℕ → ℂ} {C : ℝ} (hC : ∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C) {σ : ℝ} (hσ : 0 ≤ σ) {n : ℕ} (hn : 1 ≤ n) : ‖(∑ k ∈ Finset.range (n + 1), f k) * (↑n ^ (-↑σ) - (↑n + 1) ^ (-↑σ))‖ ≤ C * (↑n ^ (-σ) - (↑n + 1) ^ (-σ))**Per-term bound** for the real-axis by-parts series: for `1 ≤ n` and `0 ≤ σ`, the `n`-th term is at most `C` times the nonnegative increment `n ^ (-σ) - (n + 1) ^ (-σ)`.{fℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} {Cℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hC∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ (nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.), ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ) {σℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.} (hσ0 ≤ σ: 0 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.σℝ) {nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.} (hn1 ≤ n: 1 ≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.nℕ) : ‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.(∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`., fℕ → ℂkℕ) *HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).↑σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).↑σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).‖Norm.norm.{u_8} {E : Type u_8} [self : Norm E] : E → ℝthe `ℝ`-valued norm function.≤LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: `x ≤ y` Conventions for notations in identifiers: * The recommended spelling of `≤` in identifiers is `le`.Cℝ*HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γ`a * b` computes the product of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `*` in identifiers is `mul`.(HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).↑nℕ^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).(HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.↑nℕ+HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.1)HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γ`a + b` computes the sum of `a` and `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `+` in identifiers is `add`.^HPow.hPow.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HPow α β γ] : α → β → γ`a ^ b` computes `a` to the power of `b`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `^` in identifiers is `pow`.(Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).-Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).σℝ)Neg.neg.{u} {α : Type u} [self : Neg α] : α → α`-a` computes the negative or opposite of `a`. The meaning of this notation is type-dependent. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `neg` (when used as a unary operator).)HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γ`a - b` computes the difference of `a` and `b`. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: `a - b = 0` when `a ≤ b`. Conventions for notations in identifiers: * The recommended spelling of `-` in identifiers is `sub` (when used as a binary operator).
On the real axis n \mapsto n^{-\sigma} is antitone, so the increment
n^{-\sigma} - (n+1)^{-\sigma} is a nonnegative real of exactly that norm; multiply by the
coefficient bound C. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[norm_mul, norm_cpow_neg_sub_add_one_eq hσ hn]
exact`exact e` closes the main goal if its target type matches that of `e`.
mul_le_mul_of_nonneg_right (hC n)
(Real.natCast_rpow_neg_sub_add_one_nonneg hσ hn)
If the bit sequence is eventually periodic — b_{k+P} = b_k for all
k \ge N_0, with P > 0 — then there are constants c \in \mathbb{R} and C with
\bigl|\, S(N) - c\,\pi(N) \,\bigr| \;\le\; C \qquad \text{for all } N.
The slope produced by the proof is rational: c = (P - 2j)/P \in [-1,1] \cap \Q, where
j is the number of ones among b_{N_0}, \ldots, b_{N_0+P-1}.
Lean code for Theorem raceSum_linear_of_eventuallyPeriodic
-
≈
theorem raceSum_linear_of_eventuallyPeriodic : (∃ N
ℕPℕ, 0ℕ< Pℕ∧ ∀ kℕ≥ Nℕ, bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).(kℕ+ Pℕ) = bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).kℕ) → ∃ cℝCℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., ∀ Nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., |(raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of `raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes `≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`, matching the convention of `Nat.primeCounting N` (# primes `≤ N`).Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) - cℝ* (Nat.primeCountingNat.primeCounting (n : ℕ) : ℕThe prime counting function: Returns the number of primes less than or equal to the input. With `open scoped Nat.Prime`, this has notation `π`.Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.)| ≤ Cℝtheorem raceSum_linear_of_eventuallyPeriodic : (∃ N
ℕPℕ, 0ℕ< Pℕ∧ ∀ kℕ≥ Nℕ, bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).(kℕ+ Pℕ) = bitA362583.bit (k : ℕ) : ℕ`k`-th bit of the constant: `1` iff the `k`-th odd prime is `≡ 3 (mod 4)`. The `b_{k+1}` of the 1-based bit sequence; first values `1 0 1 1 0 0 1 1` (primes `3, 5, 7, 11, 13, 17, 19, 23`).kℕ) → ∃ cℝCℝ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers., ∀ Nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., |(raceSumA362583.raceSum (N : ℕ) : ℤChebyshev race sum `S(N) = Σ_{p ≤ N} χ₄(p)`, stated elementarily as the sum of `raceKernel` over primes `≤ N`: `+1` for primes `≡ 1 (mod 4)`, `-1` for primes `≡ 3 (mod 4)`, `0` for `p = 2`. The range `Finset.range (N + 1)` means primes `≤ N`, matching the convention of `Nat.primeCounting N` (# primes `≤ N`).Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.) - cℝ* (Nat.primeCountingNat.primeCounting (n : ℕ) : ℕThe prime counting function: Returns the number of primes less than or equal to the input. With `open scoped Nat.Prime`, this has notation `π`.Nℕ: ℝReal : TypeThe type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational numbers.)| ≤ Cℝ
The bridge between the race sum and the bits: the prime 2 contributes 0 to S(N), and
the k-th odd prime contributes \chi_4(p_k) = 1 - 2 b_k (+1 for a zero bit, -1 for
a one bit). For N \ge 2 there are exactly \pi(N) - 1 odd primes \le N, so
S(N) \;=\; \sum_{k < \pi(N) - 1} (1 - 2 b_k):
the race sum is a partial sum of the \pm 1 sequence read off the bits. (Translating
between "the k-th odd prime" and "primes \le N" is Mathlib's Galois connection between
the n-th-prime function and the prime-counting function.)
Now count over the periodic structure. Each full period window of length P contributes the
same window sum W = \sum_{k \in [N_0, N_0 + P)} (1 - 2 b_k) = P - 2j, so setting
c := W / P, the partial sums over m terms deviate from c\,m by a bounded amount: the
preperiodic prefix contributes at most 2 N_0, the incomplete final window at most 2P,
and each complete window deviates from cP by exactly 0. Since |1 - 2b_k| = 1 we get
|c| \le 1, and reindexing m = \pi(N) - 1 costs one more |c|, absorbed into the final
constant C = 2 N_0 + 2 P + 1. The all-ones and all-zeros patterns are simply c = -1 and
c = +1; no separate case is needed. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
rintroThe `rintro` tactic is a combination of the `intros` tactic with `rcases` to
allow for destructuring patterns while introducing variables. See `rcases` for
a description of supported patterns. For example, `rintro (a | ⟨b, c⟩) ⟨d, e⟩`
will introduce two variables, and then do case splits on both of them producing
two subgoals, one with variables `a d e` and the other with `b c d e`.
`rintro`, unlike `rcases`, also supports the form `(x y : ty)` for introducing
and type-ascripting multiple variables at once, similar to binders.
⟨N₀, P, hP, hper⟩
obtainThe `obtain` tactic is a combination of `have` and `rcases`. See `rcases` for
a description of supported patterns.
```lean
obtain ⟨patt⟩ : type := proof
```
is equivalent to
```lean
have h : type := proof
rcases h with ⟨patt⟩
```
If `⟨patt⟩` is omitted, `rcases` will try to infer the pattern.
If `type` is omitted, `:= proof` is required.
⟨c, hc1, hc⟩ := exists_c_bound hP hper
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
⟨c, 2 * (N₀ : ℝ) + 2 * (P : ℝ) + 1, fun N ↦ ?_⟩
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[raceSum_bridge N]
rcases`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
Nat.eq_zero_or_pos (Nat.primeCounting N) with`rcases` is a tactic that will perform `cases` recursively, according to a pattern. It is used to
destructure hypotheses or expressions composed of inductive types like `h1 : a ∧ b ∧ c ∨ d` or
`h2 : ∃ x y, trans_rel R x y`. Usual usage might be `rcases h1 with ⟨ha, hb, hc⟩ | hd` or
`rcases h2 with ⟨x, y, _ | ⟨z, hxz, hzy⟩⟩` for these examples.
Each element of an `rcases` pattern is matched against a particular local hypothesis (most of which
are generated during the execution of `rcases` and represent individual elements destructured from
the input expression). An `rcases` pattern has the following grammar:
* A name like `x`, which names the active hypothesis as `x`.
* A blank `_`, which does nothing (letting the automatic naming system used by `cases` name the
hypothesis).
* A hyphen `-`, which clears the active hypothesis and any dependents.
* The keyword `rfl`, which expects the hypothesis to be `h : a = b`, and calls `subst` on the
hypothesis (which has the effect of replacing `b` with `a` everywhere or vice versa).
* A type ascription `p : ty`, which sets the type of the hypothesis to `ty` and then matches it
against `p`. (Of course, `ty` must unify with the actual type of `h` for this to work.)
* A tuple pattern `⟨p1, p2, p3⟩`, which matches a constructor with many arguments, or a series
of nested conjunctions or existentials. For example if the active hypothesis is `a ∧ b ∧ c`,
then the conjunction will be destructured, and `p1` will be matched against `a`, `p2` against `b`
and so on.
* A `@` before a tuple pattern as in `@⟨p1, p2, p3⟩` will bind all arguments in the constructor,
while leaving the `@` off will only use the patterns on the explicit arguments.
* An alternation pattern `p1 | p2 | p3`, which matches an inductive type with multiple constructors,
or a nested disjunction like `a ∨ b ∨ c`.
A pattern like `⟨a, b, c⟩ | ⟨d, e⟩` will do a split over the inductive datatype,
naming the first three parameters of the first constructor as `a,b,c` and the
first two of the second constructor `d,e`. If the list is not as long as the
number of arguments to the constructor or the number of constructors, the
remaining variables will be automatically named. If there are nested brackets
such as `⟨⟨a⟩, b | c⟩ | d` then these will cause more case splits as necessary.
If there are too many arguments, such as `⟨a, b, c⟩` for splitting on
`∃ x, ∃ y, p x`, then it will be treated as `⟨a, ⟨b, c⟩⟩`, splitting the last
parameter as necessary.
`rcases` also has special support for quotient types: quotient induction into Prop works like
matching on the constructor `quot.mk`.
`rcases h : e with PAT` will do the same as `rcases e with PAT` with the exception that an
assumption `h : e = PAT` will be added to the context.
h0 | hpos
· -- π(N) = 0 (N ∈ {0, 1}): both sides vanish
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[h0]
simpThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
onlyThe `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]`, but removes the ones named `idᵢ`.
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
`hᵢ` is introduced, but the old one remains in the local context.
- `simp at *` simplifies all the hypotheses and the target.
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
other hypotheses.
[Nat.zero_sub, Finset.range_zero, Finset.sum_empty, Int.cast_zero,
Nat.cast_zero, mul_zero, sub_zero, abs_zero]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h1 : (0 : ℝ) ≤ (N₀ : ℝ) := Nat.cast_nonneg N₀
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h2 : (0 : ℝ) ≤ (P : ℝ) := Nat.cast_nonneg P
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
· -- π(N) ≥ 1: shift by one index, absorbing |c| ≤ 1 into the constant
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hsucc : (Nat.primeCounting N : ℝ) = ((Nat.primeCounting N - 1 : ℕ) : ℝ) + 1 := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h1 : Nat.primeCounting N - 1 + 1 = Nat.primeCounting N := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. omegaThe `omega` tactic, for resolving integer and natural linear arithmetic problems.
It is not yet a full decision procedure (no "dark" or "grey" shadows),
but should be effective on many problems.
We handle hypotheses of the form `x = y`, `x < y`, `x ≤ y`, and `k ∣ x` for `x y` in `Nat` or `Int`
(and `k` a literal), along with negations of these statements.
We decompose the sides of the inequalities as linear combinations of atoms.
If we encounter `x / k` or `x % k` for literal integers `k` we introduce new auxiliary variables
and the relevant inequalities.
On the first pass, we do not perform case splits on natural subtraction.
If `omega` fails, we recursively perform a case split on
a natural subtraction appearing in a hypothesis, and try again.
The options
```
omega +splitDisjunctions +splitNatSub +splitNatAbs +splitMinMax
```
can be used to:
* `splitDisjunctions`: split any disjunctions found in the context,
if the problem is not otherwise solvable.
* `splitNatSub`: for each appearance of `((a - b : Nat) : Int)`, split on `a ≤ b` if necessary.
* `splitNatAbs`: for each appearance of `Int.natAbs a`, split on `0 ≤ a` if necessary.
* `splitMinMax`: for each occurrence of `min a b`, split on `min a b = a ∨ min a b = b`
Currently, all of these are on by default.
exact_mod_castNormalize casts in the goal and the given expression, then close the goal with `exact`.
h1.symm
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hsucc]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hkey : ((∑ k ∈ Finset.range (Nat.primeCounting N - 1), term k : ℤ) : ℝ)
- c * (((Nat.primeCounting N - 1 : ℕ) : ℝ) + 1)
= (((∑ k ∈ Finset.range (Nat.primeCounting N - 1), term k : ℤ) : ℝ)
- c * ((Nat.primeCounting N - 1 : ℕ) : ℝ)) - c := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. ring`ring` solves equations in *commutative* (semi)rings, allowing for variables in the
exponent. If the goal is not appropriate for `ring` (e.g. not an equality) `ring_nf` will be
suggested. See also `ring1`, which fails if the goal is not an equality.
* `ring!` will use a more aggressive reducibility setting to determine equality of atoms.
Examples:
```
example (n : ℕ) (m : ℤ) : 2^(n+1) * m = 2 * 2^n * m := by ring
example (a b : ℤ) (n : ℕ) : (a + b)^(n + 2) = (a^2 + b^2 + a * b + b * a) * (a + b)^n := by ring
example (x y : ℕ) : x + id y = y + id x := by ring!
example (x : ℕ) (h : x * 2 > 5): x + x > 5 := by ring; assumption -- suggests ring_nf
```
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[hkey]
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h1 := hc (Nat.primeCounting N - 1)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
htri := abs_sub
(((∑ k ∈ Finset.range (Nat.primeCounting N - 1), term k : ℤ) : ℝ)
- c * ((Nat.primeCounting N - 1 : ℕ) : ℝ)) c
linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
The by-parts series of f : \mathbb{N} \to \C is
\tilde A_f(s) \;=\; \sum_{n \ge 0} \Bigl(\sum_{k \le n} f(k)\Bigr)\bigl(n^{-s} - (n+1)^{-s}\bigr).
It is what discrete Abel summation produces from the Dirichlet series
\sum_n f(n)\, n^{-s}, but here it is taken as the definition of the continuation object.
No hypothesis on f(0) is needed anywhere: Lean's junk value 0^{-s} = 0 (for
s \ne 0) makes the n = 0 term harmless, and the identification below is exact as
stated.
Lean code for Definition bpSeries
-
⟲
def bpSeries (f
ℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) (sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.def bpSeries (f
ℕ → ℂ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.→ ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) (sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.
⟲:= ∑' nℕ : ℕNat : TypeThe natural numbers, starting at zero.
This type is special-cased by both the kernel and the compiler, and overridden with an efficient
implementation. Both use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
, (∑ kℕ ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`. (nℕ + 1ℕ), fℕ → ℂ kℕ) * ((nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) ^ (-sℂ) - ((nℕ : ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. ) + 1ℂ) ^ (-sℂ))
Identification with the Dirichlet series. If the partial sums of f are bounded
by C, then for \mathrm{Re}\, s > 1
\sum_{n \ge 0} f(n)\, n^{-s} \;=\; \tilde A_f(s).
Together with the holomorphy theorem this says: the by-parts series is the analytic
continuation of the Dirichlet series from \{\mathrm{Re}\, s > 1\} to
\{\mathrm{Re}\, s > 0\} — obtained here by definition rather than by continuation, so no
measurability, integrability, or dominated-convergence side conditions ever arise.
Lean code for Theorem tsum_mul_cpow_neg_eq_bpSeries
-
≈
theorem tsum_mul_cpow_neg_eq_bpSeries (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs1 < s.re: 1ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : ∑' nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., fℕ → ℂnℕ* (nℕ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ) = bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂsℂtheorem tsum_mul_cpow_neg_eq_bpSeries (hC
∀ (n : ℕ), ‖∑ k ∈ Finset.range (n + 1), f k‖ ≤ C: ∀ nℕ, ‖∑ kℕ∈ Finset.rangeFinset.range (n : ℕ) : Finset ℕ`range n` is the set of natural numbers less than `n`.(nℕ+ 1ℕ), fℕ → ℂkℕ‖ ≤ Cℝ) {sℂ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.} (hs1 < s.re: 1ℝ< sℂ.reComplex.re (self : ℂ) : ℝThe real part of a complex number.) : ∑' nℕ: ℕNat : TypeThe natural numbers, starting at zero. This type is special-cased by both the kernel and the compiler, and overridden with an efficient implementation. Both use a fast arbitrary-precision arithmetic library (usually [GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed., fℕ → ℂnℕ* (nℕ: ℂComplex : TypeComplex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`.) ^ (-sℂ) = bpSeriesbpSeries (f : ℕ → ℂ) (s : ℂ) : ℂThe **by-parts series** attached to `f : ℕ → ℂ`: `bpSeries f s = ∑' n, (∑ k ∈ Finset.range (n + 1), f k) * ((n : ℂ) ^ (-s) - ((n : ℂ) + 1) ^ (-s))`. If the partial sums `∑ k ∈ Finset.range (n + 1), f k = ∑_{k ≤ n} f k` are bounded, this series converges and is holomorphic on `{s | 0 < s.re}` (`differentiableOn_bpSeries`) and it agrees with the Dirichlet series `∑' n, f n * (n : ℂ) ^ (-s)` for `1 < s.re` (`tsum_mul_cpow_neg_eq_bpSeries`): it is the analytic continuation of that Dirichlet series to the right half-plane. This is the analytic-continuation object `Ã` of the proof.fℕ → ℂsℂ
Bounded partial sums bound the coefficients, \|f(n)\| \le 2C (consecutive partial sums
differ by f(n)), so the Dirichlet series \sum_n f(n)\, n^{-s} converges absolutely for
\mathrm{Re}\, s > 1 (domination by 2C\, n^{-\mathrm{Re}\,s}). Finite Abel summation
(summation by parts) gives, for every N,
\sum_{i \le N} f(i)\, i^{-s} \;=\; \Bigl(\sum_{i \le N} f(i)\Bigr) N^{-s}
\;+\; \sum_{i < N} \Bigl(\sum_{k \le i} f(k)\Bigr)\bigl(i^{-s} - (i+1)^{-s}\bigr).
Let N \to \infty. The left side tends to \sum_n f(n)\, n^{-s}; the boundary term tends to
0, since
\bigl\|\bigl(\sum_{k \le N} f(k)\bigr) N^{-s}\bigr\| \le C\, N^{-\mathrm{Re}\,s} \to 0;
and the by-parts partial sum tends to \tilde A_f(s). Uniqueness of limits yields the
identity. No hypothesis on f(0) is needed: the n = 0 Dirichlet term vanishes because
0^{-s} = 0 for s \ne 0. \blacksquare
≈by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hs0 : 0 < s.re := zero_lt_one.trans hs
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hC0 : 0 ≤ C := (norm_nonneg _).trans (hC 0)
-- coefficient bound from bounded partial sums
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hcoef := norm_le_two_mul_of_partialSum_le hC
-- the Dirichlet series converges for `1 < s.re`
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
hD : Summable fun n : ℕ ↦ f n * (n : ℂ) ^ (-s) := by`by tac` constructs a term of the expected type by running the tactic(s) `tac`.
refine`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
holes in `e` that are not solved by unification with the main goal's target type
are converted into new goals, using the hole's name, if any, as the goal case name.
Summable.of_norm_bounded_eventually_nat
(g := fun n : ℕ ↦ 2 * C * (n : ℝ) ^ (-s.re))
((Real.summable_nat_rpow.2 (by`by tac` constructs a term of the expected type by running the tactic(s) `tac`. linarith`linarith` attempts to find a contradiction between hypotheses that are linear (in)equalities.
Equivalently, it can prove a linear inequality by assuming its negation and proving `False`.
In theory, `linarith` should prove any goal that is true in the theory of linear arithmetic over
the rationals. While there is some special handling for non-dense orders like `Nat` and `Int`,
this tactic is not complete for these theories and will not prove every true goal. It will solve
goals over arbitrary types that instantiate `CommRing`, `LinearOrder` and `IsStrictOrderedRing`.
An example:
```lean
example (x y z : ℚ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0)
(h3 : 12*y - 4* z < 0) : False := by
linarith
```
`linarith` will use all appropriate hypotheses and the negation of the goal, if applicable.
Disequality hypotheses require case splitting and are not normally considered
(see the `splitNe` option below).
`linarith [t1, t2, t3]` will additionally use proof terms `t1, t2, t3`.
`linarith only [h1, h2, h3, t1, t2, t3]` will use only the goal (if relevant), local hypotheses
`h1`, `h2`, `h3`, and proofs `t1`, `t2`, `t3`. It will ignore the rest of the local context.
`linarith!` will use a stronger reducibility setting to try to identify atoms. For example,
```lean
example (x : ℚ) : id x ≥ x := by
linarith
```
will fail, because `linarith` will not identify `x` and `id x`. `linarith!` will.
This can sometimes be expensive.
`linarith (config := { .. })` takes a config object with five
optional arguments:
* `discharger` specifies a tactic to be used for reducing an algebraic equation in the
proof stage. The default is `ring`. Other options include `simp` for basic
problems.
* `transparency` controls how hard `linarith` will try to match atoms to each other. By default
it will only unfold `reducible` definitions.
* If `splitHypotheses` is true, `linarith` will split conjunctions in the context into separate
hypotheses.
* If `splitNe` is `true`, `linarith` will case split on disequality hypotheses.
For a given `x ≠ y` hypothesis, `linarith` is run with both `x < y` and `x > y`,
and so this runs linarith exponentially many times with respect to the number of
disequality hypotheses. (`false` by default.)
* If `exfalso` is `false`, `linarith` will fail when the goal is neither an inequality nor `False`.
(`true` by default.)
* If `minimize` is `false`, `linarith?` will report all hypotheses appearing in its initial
proof without attempting to drop redundancies. (`true` by default.)
* `restrict_type` (not yet implemented in mathlib4)
will only use hypotheses that are inequalities over `tp`. This is useful
if you have e.g. both integer- and rational-valued inequalities in the local context, which can
sometimes confuse the tactic.
A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
The option `set_option trace.linarith true` will trace certain intermediate stages of the `linarith`
routine.
)).mul_left _) ?_
filter_upwards`filter_upwards [h₁, ⋯, hₙ]` replaces a goal of the form `s ∈ f` and terms
`h₁ : t₁ ∈ f, ⋯, hₙ : tₙ ∈ f` with `∀ x, x ∈ t₁ → ⋯ → x ∈ tₙ → x ∈ s`.
The list is an optional parameter, `[]` being its default value.
`filter_upwards [h₁, ⋯, hₙ] with a₁ a₂ ⋯ aₖ` is a short form for
`{ filter_upwards [h₁, ⋯, hₙ], intro a₁ a₂ ⋯ aₖ }`.
`filter_upwards [h₁, ⋯, hₙ] using e` is a short form for
`{ filter_upwards [h1, ⋯, hn], exact e }`.
Combining both shortcuts is done by writing `filter_upwards [h₁, ⋯, hₙ] with a₁ a₂ ⋯ aₖ using e`.
Note that in this case, the `aᵢ` terms can be used in `e`.
[eventually_ge_atTop 1] with`filter_upwards [h₁, ⋯, hₙ]` replaces a goal of the form `s ∈ f` and terms
`h₁ : t₁ ∈ f, ⋯, hₙ : tₙ ∈ f` with `∀ x, x ∈ t₁ → ⋯ → x ∈ tₙ → x ∈ s`.
The list is an optional parameter, `[]` being its default value.
`filter_upwards [h₁, ⋯, hₙ] with a₁ a₂ ⋯ aₖ` is a short form for
`{ filter_upwards [h₁, ⋯, hₙ], intro a₁ a₂ ⋯ aₖ }`.
`filter_upwards [h₁, ⋯, hₙ] using e` is a short form for
`{ filter_upwards [h1, ⋯, hn], exact e }`.
Combining both shortcuts is done by writing `filter_upwards [h₁, ⋯, hₙ] with a₁ a₂ ⋯ aₖ using e`.
Note that in this case, the `aᵢ` terms can be used in `e`.
n hn
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[norm_mul, Complex.norm_natCast_cpow_of_pos hn, Complex.neg_re]
exact`exact e` closes the main goal if its target type matches that of `e`.
mul_le_mul_of_nonneg_right (hcoef n) (Real.rpow_nonneg (Nat.cast_nonneg n) _)
-- limits of both sides of the finite identity
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h1 : Tendsto (fun N : ℕ ↦ ∑ i ∈ Finset.range (N + 1), f i * (i : ℂ) ^ (-s)) atTop
(𝓝 (∑' n : ℕ, f n * (n : ℂ) ^ (-s))) :=
hD.hasSum.tendsto_sum_nat.comp (tendsto_add_atTop_nat 1)
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h2 := tendsto_boundary_term hC hs0
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h3 : Tendsto (fun N : ℕ ↦ ∑ i ∈ Finset.range N,
(∑ k ∈ Finset.range (i + 1), f k) * ((i : ℂ) ^ (-s) - ((i : ℂ) + 1) ^ (-s))) atTop
(𝓝 (bpSeries f s)) :=
(hasSum_bpSeries hC hs0).tendsto_sum_nat
haveThe `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.
The definitions forget their associated value and cannot be unfolded, unlike definitions added by the `let` tactic.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
* The syntax `have (eq := h) pat := e` is equivalent to `match h : e with | pat => _`,
which adds the equation `h : e = pat` to the local context.
The tactic supports all the same syntax variants and options as the `have` term.
## Properties and relations
* It is not possible to unfold a variable introduced using `have`, since the definition's value is forgotten.
The `let` tactic introduces definitions that can be unfolded.
* The `have h : t := e` is like doing `let h : t := e; clear_value h`.
* The `have` tactic is preferred for propositions, and `let` is preferred for non-propositions.
* Sometimes `have` is used for non-propositions to ensure that the variable is never unfolded,
which may be important for performance reasons.
Consider using the equivalent `let +nondep` to indicate the intent.
h4 := h2.add h3
rw`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
[zero_add] atLocation specifications are used by many tactics that can operate on either the
hypotheses or the goal. It can have one of the forms:
* 'empty' is not actually present in this syntax, but most tactics use
`(location)?` matchers. It means to target the goal only.
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
* `at *`: target all hypotheses and the goal
h4
exact`exact e` closes the main goal if its target type matches that of `e`.
tendsto_nhds_unique h1 (h4.congr fun N ↦ (sum_range_mul_cpow_eq N).symm)