File: Mathlib/Tactic/TFAE.lean

full_name: Mathlib.Tactic.TFAE.impArrow
code: /-- An arrow of the form `←`, `→`, or `↔`. -/
syntax impArrow := " → " <|> " ↔ " <|> " ← "
goal: Lean.ParserDescr

full_name: Mathlib.Tactic.TFAE.tfaeHave
code: /--
`tfae_have` introduces hypotheses for proving goals of the form `TFAE [P₁, P₂, ...]`. Specifically,
`tfae_have i arrow j` introduces a hypothesis of type `Pᵢ arrow Pⱼ` to the local context,
where `arrow` can be `→`, `←`, or `↔`. Note that `i` and `j` are natural number indices (beginning
at 1) used to specify the propositions `P₁, P₂, ...` that appear in the `TFAE` goal list. A proof
is required afterward, typically via a tactic block.

```lean
example (h : P → R) : TFAE [P, Q, R] := by
  tfae_have 1 → 3
  · exact h
  ...
```
The resulting context now includes `tfae_1_to_3 : P → R`.

The introduced hypothesis can be given a custom name, in analogy to `have` syntax:
```lean
tfae_have h : 2 ↔ 3
```

Once sufficient hypotheses have been introduced by `tfae_have`, `tfae_finish` can be used to close
the goal.

```lean
example : TFAE [P, Q, R] := by
  tfae_have 1 → 2
  · /- proof of P → Q -/
  tfae_have 2 → 1
  · /- proof of Q → P -/
  tfae_have 2 ↔ 3
  · /- proof of Q ↔ R -/
  tfae_finish
```
-/
syntax (name := tfaeHave) "tfae_have " (ident " : ")? num impArrow num : tactic
goal: Lean.ParserDescr

full_name: Mathlib.Tactic.TFAE.tfaeFinish
code: /--
`tfae_finish` is used to close goals of the form `TFAE [P₁, P₂, ...]` once a sufficient collection
of hypotheses of the form `Pᵢ → Pⱼ` or `Pᵢ ↔ Pⱼ` have been introduced to the local context.

`tfae_have` can be used to conveniently introduce these hypotheses; see `tfae_have`.

Example:
```lean
example : TFAE [P, Q, R] := by
  tfae_have 1 → 2
  · /- proof of P → Q -/
  tfae_have 2 → 1
  · /- proof of Q → P -/
  tfae_have 2 ↔ 3
  · /- proof of Q ↔ R -/
  tfae_finish
```
-/
syntax (name := tfaeFinish) "tfae_finish" : tactic
goal: Lean.ParserDescr

full_name: Mathlib.Tactic.TFAE.getTFAEList.getExplicitList
code: /-- Extract a list of `Prop` expressions from an expression of the form `TFAE [P₁, P₂, ...]` as
long as `[P₁, P₂, ...]` is an explicit list. -/
partial def getTFAEList (t : Expr) : MetaM (Q(List Prop) × List Q(Prop)) 
goal: Lean.MetaM (List Q(Prop))

full_name: Mathlib.Tactic.TFAE.getTFAEList
code: /-- Extract a list of `Prop` expressions from an expression of the form `TFAE [P₁, P₂, ...]` as
long as `[P₁, P₂, ...]` is an explicit list. -/
partial def getTFAEList (t : Expr) : MetaM (Q(List Prop) × List Q(Prop)) 
goal: Lean.MetaM (Q(List Prop) × List Q(Prop))

full_name: Mathlib.Tactic.TFAE.dfs
code: /-- Uses depth-first search to find a path from `P` to `P'`. -/
partial def dfs (i j : ℕ) (P P' : Q(Prop)) (hP : Q($P)) : StateT (HashSet ℕ) MetaM Q($P') 
goal: StateT (Lean.HashSet ℕ) Lean.MetaM Q(«$P'»)

full_name: Mathlib.Tactic.TFAE.proveImpl
code: /-- Prove an implication via depth-first traversal. -/
def proveImpl (i j : ℕ) (P P' : Q(Prop)) : MetaM Q($P → $P') 
goal: Lean.MetaM Q(«$P» → «$P'»)

full_name: Mathlib.Tactic.TFAE.proveChain
code: /-- Generate a proof of `Chain (· → ·) P l`. We assume `P : Prop` and `l : List Prop`, and that `l`
is an explicit list. -/
partial def proveChain (i : ℕ) (is : List ℕ) (P : Q(Prop)) (l : Q(List Prop)) :
    MetaM Q(Chain (· → ·) $P $l) 
goal: Lean.MetaM Q(List.Chain (fun x x_1 => x → x_1) «$P» «$l»)

full_name: Mathlib.Tactic.TFAE.proveGetLastDImpl
code: /-- Attempt to prove `getLastD l P' → P` given an explicit list `l`. -/
partial def proveGetLastDImpl (i i' : ℕ) (is : List ℕ) (P P' : Q(Prop)) (l : Q(List Prop)) :
    MetaM Q(getLastD $l $P' → $P) 
goal: Lean.MetaM Q(«$l».getLastD «$P'» → «$P»)

full_name: Mathlib.Tactic.TFAE.proveTFAE
code: /-- Attempt to prove a statement of the form `TFAE [P₁, P₂, ...]`. -/
def proveTFAE (is : List ℕ) (l : Q(List Prop)) : MetaM Q(TFAE $l) 
goal: Lean.MetaM Q(«$l».TFAE)

full_name: Mathlib.Tactic.TFAE.mkTFAEHypName
code: /-- Construct a name for a hypothesis introduced by `tfae_have`. -/
def mkTFAEHypName (i j : TSyntax `num) (arr : TSyntax ``impArrow) : MetaM Name 
goal: Lean.MetaM Lean.Name

full_name: Mathlib.Tactic.TFAE.tfaeHaveCore
code: /-- The core of `tfae_have`, which behaves like `haveLetCore` in `Mathlib.Tactic.Have`. -/
def tfaeHaveCore (goal : MVarId) (name : Option (TSyntax `ident)) (i j : TSyntax `num)
    (arrow : TSyntax ``impArrow) (t : Expr) : TermElabM (MVarId × MVarId) 
goal: Lean.Elab.TermElabM (Lean.MVarId × Lean.MVarId)

full_name: Mathlib.Tactic.TFAE.elabIndex
code: /-- Turn syntax for a given index into a natural number, as long as it lies between `1` and
`maxIndex`. -/
def elabIndex (i : TSyntax `num) (maxIndex : ℕ) : TacticM ℕ 
goal: Lean.Elab.Tactic.TacticM ℕ

full_name: Mathlib.Tactic.TFAE.mkImplType
code: /-- Construct an expression for the type `Pj → Pi`, `Pi → Pj`, or `Pi ↔ Pj` given expressions
`Pi Pj : Q(Prop)` and `impArrow` syntax `arr`, depending on whether `arr` is `←`, `→`, or `↔`
respectively. -/
def mkImplType (Pi : Q(Prop)) (arr : TSyntax ``impArrow) (Pj : Q(Prop)) : MetaM Q(Prop) 
goal: Lean.MetaM Q(Prop)

full_name: Mathlib.Tactic.TFAE._aux_Mathlib_Tactic_TFAE___elabRules_Mathlib_Tactic_TFAE_tfaeHave_1
code: elab_rules : tactic
| `(tactic| tfae_have $[$h:ident : ]? $i:num $arr:impArrow $j:num) => do
  let goal ← getMainGoal
  goal.withContext do
    let (_, tfaeList) ← getTFAEList (← goal.getType)
    let l₀ := tfaeList.length
    let i' ← elabIndex i l₀
    let j' ← elabIndex j l₀
    let Pi := tfaeList.get! (i'-1)
    let Pj := tfaeList.get! (j'-1)
    let type ← mkImplType Pi arr Pj
    let (goal1, goal2) ← tfaeHaveCore goal h i j arr type
    replaceMainGoal [goal1, goal2]
goal: Lean.Elab.Tactic.Tactic

full_name: Mathlib.Tactic.TFAE._aux_Mathlib_Tactic_TFAE___elabRules_Mathlib_Tactic_TFAE_tfaeFinish_1
code: elab_rules : tactic
| `(tactic| tfae_finish) => do
  let goal ← getMainGoal
  goal.withContext do
    let (tfaeListQ, tfaeList) ← getTFAEList (← goal.getType)
    closeMainGoal <|← AtomM.run .reducible do
      let is ← tfaeList.mapM AtomM.addAtom
      let mut hyps := #[]
      for hyp in ← getLocalHyps do
        let ty ← inferType hyp
        if let (``Iff, #[p1, p2]) := ty.getAppFnArgs then
          let q1 ← AtomM.addAtom p1
          let q2 ← AtomM.addAtom p2
          hyps := hyps.push (q1, q2, ← mkAppM ``Iff.mp #[hyp])
          hyps := hyps.push (q2, q1, ← mkAppM ``Iff.mpr #[hyp])
        else if ty.isArrow then
          let q1 ← AtomM.addAtom ty.bindingDomain!
          let q2 ← AtomM.addAtom ty.bindingBody!
          hyps := hyps.push (q1, q2, hyp)
      proveTFAE hyps (← get).atoms is tfaeListQ
goal: Lean.Elab.Tactic.Tactic