File: Mathlib/Tactic/Peel.lean

full_name: Mathlib.Tactic.Peel.peel
code: /--
Peels matching quantifiers off of a given term and the goal and introduces the relevant variables.

- `peel e` peels all quantifiers (at reducible transparency),
  using `this` for the name of the peeled hypothesis.
- `peel e with h` is `peel e` but names the peeled hypothesis `h`.
  If `h` is `_` then uses `this` for the name of the peeled hypothesis.
- `peel n e` peels `n` quantifiers (at default transparency).
- `peel n e with x y z ... h` peels `n` quantifiers, names the peeled hypothesis `h`,
  and uses `x`, `y`, `z`, and so on to name the introduced variables; these names may be `_`.
  If `h` is `_` then uses `this` for the name of the peeled hypothesis.
  The length of the list of variables does not need to equal `n`.
- `peel e with x₁ ... xₙ h` is `peel n e with x₁ ... xₙ h`.

There are also variants that apply to an iff in the goal:
- `peel n` peels `n` quantifiers in an iff.
- `peel with x₁ ... xₙ` peels `n` quantifiers in an iff and names them.

Given `p q : ℕ → Prop`, `h : ∀ x, p x`, and a goal `⊢ : ∀ x, q x`, the tactic `peel h with x h'`
will introduce `x : ℕ`, `h' : p x` into the context and the new goal will be `⊢ q x`. This works
with `∃`, as well as `∀ᶠ` and `∃ᶠ`, and it can even be applied to a sequence of quantifiers. Note
that this is a logically weaker setup, so using this tactic is not always feasible.

For a more complex example, given a hypothesis and a goal:
```
h : ∀ ε > (0 : ℝ), ∃ N : ℕ, ∀ n ≥ N, 1 / (n + 1 : ℝ) < ε
⊢ ∀ ε > (0 : ℝ), ∃ N : ℕ, ∀ n ≥ N, 1 / (n + 1 : ℝ) ≤ ε
```
(which differ only in `<`/`≤`), applying `peel h with ε hε N n hn h_peel` will yield a tactic state:
```
h : ∀ ε > (0 : ℝ), ∃ N : ℕ, ∀ n ≥ N, 1 / (n + 1 : ℝ) < ε
ε : ℝ
hε : 0 < ε
N n : ℕ
hn : N ≤ n
h_peel : 1 / (n + 1 : ℝ) < ε
⊢ 1 / (n + 1 : ℝ) ≤ ε
```
and the goal can be closed with `exact h_peel.le`.
Note that in this example, `h` and the goal are logically equivalent statements, but `peel`
*cannot* be immediately applied to show that the goal implies `h`.

In addition, `peel` supports goals of the form `(∀ x, p x) ↔ ∀ x, q x`, or likewise for any
other quantifier. In this case, there is no hypothesis or term to supply, but otherwise the syntax
is the same. So for such goals, the syntax is `peel 1` or `peel with x`, and after which the
resulting goal is `p x ↔ q x`. The `congr!` tactic can also be applied to goals of this form using
`congr! 1 with x`. While `congr!` applies congruence lemmas in general, `peel` can be relied upon
to only apply to outermost quantifiers.

Finally, the user may supply a term `e` via `... using e` in order to close the goal
immediately. In particular, `peel h using e` is equivalent to `peel h; exact e`. The `using` syntax
may be paired with any of the other features of `peel`.

This tactic works by repeatedly applying lemmas such as `forall_imp`, `Exists.imp`,
`Filter.Eventually.mp`, `Filter.Frequently.mp`, and `Filter.eventually_of_forall`.
-/
syntax (name := peel)
  "peel" (num)? (ppSpace colGt term)?
  (" with" (ppSpace colGt (ident <|> hole))+)? (usingArg)? : tactic
goal: Lean.ParserDescr

full_name: _private.Mathlib.Tactic.Peel.0.Mathlib.Tactic.Peel.and_imp_left_of_imp_imp
code: private lemma and_imp_left_of_imp_imp {p q r : Prop} (h : r → p → q) : r ∧ p → r ∧ q 
goal: r ∧ p → r ∧ q

full_name: _private.Mathlib.Tactic.Peel.0.Mathlib.Tactic.Peel.eventually_imp
code: private theorem eventually_imp {α : Type*} {p q : α → Prop} {f : Filter α}
    (hq : ∀ (x : α), p x → q x) (hp : ∀ᶠ (x : α) in f, p x) : ∀ᶠ (x : α) in f, q x 
goal: ∀ᶠ (x : α) in f, q x

full_name: _private.Mathlib.Tactic.Peel.0.Mathlib.Tactic.Peel.frequently_imp
code: private theorem frequently_imp {α : Type*} {p q : α → Prop} {f : Filter α}
    (hq : ∀ (x : α), p x → q x) (hp : ∃ᶠ (x : α) in f, p x) : ∃ᶠ (x : α) in f, q x 
goal: ∃ᶠ (x : α) in f, q x

full_name: _private.Mathlib.Tactic.Peel.0.Mathlib.Tactic.Peel.eventually_congr
code: private theorem eventually_congr {α : Type*} {p q : α → Prop} {f : Filter α}
    (hq : ∀ (x : α), p x ↔ q x) : (∀ᶠ (x : α) in f, p x) ↔ ∀ᶠ (x : α) in f, q x 
goal: (∀ᶠ (x : α) in f, p x) ↔ ∀ᶠ (x : α) in f, q x

full_name: _private.Mathlib.Tactic.Peel.0.Mathlib.Tactic.Peel.frequently_congr
code: private theorem frequently_congr {α : Type*} {p q : α → Prop} {f : Filter α}
    (hq : ∀ (x : α), p x ↔ q x) : (∃ᶠ (x : α) in f, p x) ↔ ∃ᶠ (x : α) in f, q x 
goal: (∃ᶠ (x : α) in f, p x) ↔ ∃ᶠ (x : α) in f, q x

full_name: Mathlib.Tactic.Peel.quantifiers
code: /-- The list of constants that are regarded as being quantifiers. -/
def quantifiers : List Name 
goal: List Lean.Name

full_name: Mathlib.Tactic.Peel.whnfQuantifier
code: /-- If `unfold` is false then do `whnfR`, otherwise unfold everything that's not a quantifier,
according to the `quantifiers` list. -/
def whnfQuantifier (p : Expr) (unfold : Bool) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.Peel.throwPeelError
code: /-- Throws an error saying `ty` and `target` could not be matched up. -/
def throwPeelError {α : Type} (ty target : Expr) : MetaM α 
goal: Lean.MetaM α

full_name: Mathlib.Tactic.Peel.mkFreshBinderName
code: /-- If `f` is a lambda then use its binding name to generate a new hygienic name,
and otherwise choose a new hygienic name. -/
def mkFreshBinderName (f : Expr) : MetaM Name 
goal: Lean.MetaM Lean.Name

full_name: Mathlib.Tactic.Peel.applyPeelThm
code: /-- Applies a "peel theorem" with two main arguments, where the first is the new goal
and the second can be filled in using `e`. Then it intros two variables with the
provided names.

If, for example, `goal : ∃ y : α, q y` and `thm := Exists.imp`, the metavariable returned has
type `q x` where `x : α` has been introduced into the context. -/
def applyPeelThm (thm : Name) (goal : MVarId)
    (e : Expr) (ty target : Expr) (n : Name) (n' : Name) :
    MetaM (FVarId × List MVarId) 
goal: Lean.MetaM (Lean.FVarId × List Lean.MVarId)

full_name: Mathlib.Tactic.Peel.peelCore
code: /-- This is the core to the `peel` tactic.

It tries to match `e` and `goal` as quantified statements (using `∀` and the quantifiers in
the `quantifiers` list), then applies "peel theorems" using `applyPeelThm`.

We treat `∧` as a quantifier for sake of dealing with quantified statements
like `∃ δ > (0 : ℝ), q δ`, which is notation for `∃ δ, δ > (0 : ℝ) ∧ q δ`. -/
def peelCore (goal : MVarId) (e : Expr) (n? : Option Name) (n' : Name) (unfold : Bool) :
    MetaM (FVarId × List MVarId) 
goal: Lean.MetaM (Lean.FVarId × List Lean.MVarId)

full_name: Mathlib.Tactic.Peel.peelArgs
code: /-- Given a list `l` of names, this peels `num` quantifiers off of the expression `e` and
the main goal and introduces variables with the provided names until the list of names is exhausted.
Note: the name `n?` (with default `this`) is used for the name of the expression `e` with
quantifiers peeled. -/
def peelArgs (e : Expr) (num : Nat) (l : List Name) (n? : Option Name) (unfold : Bool := true) :
    TacticM Unit 
goal: Lean.Elab.Tactic.TacticM Unit

full_name: Mathlib.Tactic.Peel.peelUnbounded
code: /-- Similar to `peelArgs` but peels arbitrarily many quantifiers. Returns whether or not
any quantifiers were peeled. -/
partial def peelUnbounded (e : Expr) (n? : Option Name) (unfold : Bool := false) :
    TacticM Bool 
goal: Lean.Elab.Tactic.TacticM Bool

full_name: Mathlib.Tactic.Peel.peelIffAux
code: /-- Peel off a single quantifier from an `↔`. -/
def peelIffAux : TacticM Unit 
goal: Lean.Elab.Tactic.TacticM Unit

full_name: Mathlib.Tactic.Peel.peelArgsIff
code: /-- Peel off quantifiers from an `↔` and assign the names given in `l` to the introduced
variables. -/
def peelArgsIff (l : List Name) : TacticM Unit 
goal: Lean.Elab.Tactic.TacticM Unit

full_name: Mathlib.Tactic.Peel._aux_Mathlib_Tactic_Peel___elabRules_Mathlib_Tactic_Peel_peel_1
code: elab_rules : tactic
  | `(tactic| peel $[$num?:num]? $e:term $[with $l?* $n?]?) => withMainContext do
    /- we use `elabTermForApply` instead of `elabTerm` so that terms passed to `peel` can contain
    quantifiers with implicit bound variables without causing errors or requiring `@`.  -/
    let e ← elabTermForApply e false
    let n? := n?.bind fun n => if n.raw.isIdent then pure n.raw.getId else none
    let l := (l?.getD #[]).map getNameOfIdent' |>.toList
    -- If num is not present and if there are any provided variable names,
    -- use the number of variable names.
    let num? := num?.map (·.getNat) <|> if l.isEmpty then none else l.length
    if let some num := num? then
      peelArgs e num l n?
    else
      unless ← peelUnbounded e n? do
        throwPeelError (← inferType e) (← getMainTarget)
  | `(tactic| peel $n:num) => peelArgsIff <| .replicate n.getNat `_
  | `(tactic| peel with $args*) => peelArgsIff (args.map getNameOfIdent').toList
goal: Lean.Elab.Tactic.Tactic

full_name: Mathlib.Tactic.Peel._aux_Mathlib_Tactic_Peel___macroRules_Mathlib_Tactic_Peel_peel_1
code: macro_rules
  | `(tactic| peel $[$n:num]? $[$e:term]? $[with $h*]? using $u:term) =>
    `(tactic| peel $[$n:num]? $[$e:term]? $[with $h*]?; exact $u)
goal: Lean.Macro