File: Mathlib/RingTheory/WittVector/IsPoly.lean

full_name: WittVector._aux_Mathlib_RingTheory_WittVector_IsPoly___unexpand_WittVector_1
code: local notation "𝕎" => WittVector p
goal: Lean.PrettyPrinter.Unexpander

full_name: WittVector._aux_Mathlib_RingTheory_WittVector_IsPoly___macroRules_WittVector_term𝕎_1
code: local notation "𝕎" => WittVector p
goal: Lean.Macro

full_name: WittVector.poly_eq_of_wittPolynomial_bind_eq'
code: theorem poly_eq_of_wittPolynomial_bind_eq' [Fact p.Prime] (f g : ℕ → MvPolynomial (idx × ℕ) ℤ)
    (h : ∀ n, bind₁ f (wittPolynomial p _ n) = bind₁ g (wittPolynomial p _ n)) : f = g 
goal: f = g

full_name: WittVector.poly_eq_of_wittPolynomial_bind_eq
code: theorem poly_eq_of_wittPolynomial_bind_eq [Fact p.Prime] (f g : ℕ → MvPolynomial ℕ ℤ)
    (h : ∀ n, bind₁ f (wittPolynomial p _ n) = bind₁ g (wittPolynomial p _ n)) : f = g 
goal: f = g

full_name: WittVector.IsPoly
code: /--
A function `f : Π R, 𝕎 R → 𝕎 R` that maps Witt vectors to Witt vectors over arbitrary base rings
is said to be *polynomial* if there is a family of polynomials `φₙ` over `ℤ` such that the `n`th
coefficient of `f x` is given by evaluating `φₙ` at the coefficients of `x`.

See also `WittVector.IsPoly₂` for the binary variant.

The `ghost_calc` tactic makes use of the `IsPoly` and `IsPoly₂` typeclass and its instances.
(In Lean 3, there was an `@[is_poly]` attribute to manage these instances,
because typeclass resolution did not play well with function composition.
This no longer seems to be an issue, so that such instances can be defined directly.)
-/
class IsPoly (f : ∀ ⦃R⦄ [CommRing R], WittVector p R → 𝕎 R) : Prop where mk' ::
  poly :
    ∃ φ : ℕ → MvPolynomial ℕ ℤ,
      ∀ ⦃R⦄ [CommRing R] (x : 𝕎 R), (f x).coeff = fun n => aeval x.coeff (φ n)
goal: Prop

full_name: WittVector.IsPoly.mk'
code: /--
A function `f : Π R, 𝕎 R → 𝕎 R` that maps Witt vectors to Witt vectors over arbitrary base rings
is said to be *polynomial* if there is a family of polynomials `φₙ` over `ℤ` such that the `n`th
coefficient of `f x` is given by evaluating `φₙ` at the coefficients of `x`.

See also `WittVector.IsPoly₂` for the binary variant.

The `ghost_calc` tactic makes use of the `IsPoly` and `IsPoly₂` typeclass and its instances.
(In Lean 3, there was an `@[is_poly]` attribute to manage these instances,
because typeclass resolution did not play well with function composition.
This no longer seems to be an issue, so that such instances can be defined directly.)
-/
class IsPoly (f : ∀ ⦃R⦄ [CommRing R], WittVector p R → 𝕎 R) : Prop where mk' ::
  poly :
    ∃ φ : ℕ → MvPolynomial ℕ ℤ,
      ∀ ⦃R⦄ [CommRing R] (x : 𝕎 R), (f x).coeff = fun n => aeval x.coeff (φ n)
goal: WittVector.IsPoly p f

full_name: WittVector.IsPoly.poly
code: /--
A function `f : Π R, 𝕎 R → 𝕎 R` that maps Witt vectors to Witt vectors over arbitrary base rings
is said to be *polynomial* if there is a family of polynomials `φₙ` over `ℤ` such that the `n`th
coefficient of `f x` is given by evaluating `φₙ` at the coefficients of `x`.

See also `WittVector.IsPoly₂` for the binary variant.

The `ghost_calc` tactic makes use of the `IsPoly` and `IsPoly₂` typeclass and its instances.
(In Lean 3, there was an `@[is_poly]` attribute to manage these instances,
because typeclass resolution did not play well with function composition.
This no longer seems to be an issue, so that such instances can be defined directly.)
-/
class IsPoly (f : ∀ ⦃R⦄ [CommRing R], WittVector p R → 𝕎 R) : Prop where mk' ::
  poly :
    ∃ φ : ℕ → MvPolynomial ℕ ℤ,
      ∀ ⦃R⦄ [CommRing R] (x : 𝕎 R), (f x).coeff = fun n => aeval x.coeff (φ n)
goal: ∃ φ,
  ∀ ⦃R : Type u_3⦄ [inst : CommRing R] (x : WittVector p R), (f x).coeff = fun n => (MvPolynomial.aeval x.coeff) (φ n)

full_name: WittVector.idIsPoly
code: /-- The identity function on Witt vectors is a polynomial function. -/
instance idIsPoly : IsPoly p fun _ _ => id 
goal: WittVector.IsPoly p fun x x_1 => id

full_name: WittVector.idIsPolyI'
code: instance idIsPolyI' : IsPoly p fun _ _ a => a 
goal: WittVector.IsPoly p fun x x_1 a => a

full_name: WittVector.IsPoly.instInhabitedId
code: instance : Inhabited (IsPoly p fun _ _ => id) 
goal: Inhabited (WittVector.IsPoly p fun x x_1 => id)

full_name: WittVector.IsPoly.ext
code: theorem ext [Fact p.Prime] {f g} (hf : IsPoly p f) (hg : IsPoly p g)
    (h : ∀ (R : Type u) [_Rcr : CommRing R] (x : 𝕎 R) (n : ℕ),
        ghostComponent n (f x) = ghostComponent n (g x)) :
    ∀ (R : Type u) [_Rcr : CommRing R] (x : 𝕎 R), f x = g x 
goal: f x = g x

full_name: WittVector.IsPoly.comp
code: /-- The composition of polynomial functions is polynomial. -/
-- Porting note (#10754): made this an instance
instance comp {g f} [hg : IsPoly p g] [hf : IsPoly p f] :
    IsPoly p fun R _Rcr => @g R _Rcr ∘ @f R _Rcr 
goal: WittVector.IsPoly p fun R _Rcr => g ∘ f

full_name: WittVector.IsPoly₂.mk'
code: /-- A binary function `f : Π R, 𝕎 R → 𝕎 R → 𝕎 R` on Witt vectors
is said to be *polynomial* if there is a family of polynomials `φₙ` over `ℤ` such that the `n`th
coefficient of `f x y` is given by evaluating `φₙ` at the coefficients of `x` and `y`.

See also `WittVector.IsPoly` for the unary variant.

The `ghost_calc` tactic makes use of the `IsPoly` and `IsPoly₂` typeclass and its instances.
(In Lean 3, there was an `@[is_poly]` attribute to manage these instances,
because typeclass resolution did not play well with function composition.
This no longer seems to be an issue, so that such instances can be defined directly.)
-/
class IsPoly₂ (f : ∀ ⦃R⦄ [CommRing R], WittVector p R → 𝕎 R → 𝕎 R) : Prop where mk' ::
  poly :
    ∃ φ : ℕ → MvPolynomial (Fin 2 × ℕ) ℤ,
      ∀ ⦃R⦄ [CommRing R] (x y : 𝕎 R), (f x y).coeff = fun n => peval (φ n) ![x.coeff, y.coeff]
goal: WittVector.IsPoly₂ p f

full_name: WittVector.IsPoly₂.poly
code: /-- A binary function `f : Π R, 𝕎 R → 𝕎 R → 𝕎 R` on Witt vectors
is said to be *polynomial* if there is a family of polynomials `φₙ` over `ℤ` such that the `n`th
coefficient of `f x y` is given by evaluating `φₙ` at the coefficients of `x` and `y`.

See also `WittVector.IsPoly` for the unary variant.

The `ghost_calc` tactic makes use of the `IsPoly` and `IsPoly₂` typeclass and its instances.
(In Lean 3, there was an `@[is_poly]` attribute to manage these instances,
because typeclass resolution did not play well with function composition.
This no longer seems to be an issue, so that such instances can be defined directly.)
-/
class IsPoly₂ (f : ∀ ⦃R⦄ [CommRing R], WittVector p R → 𝕎 R → 𝕎 R) : Prop where mk' ::
  poly :
    ∃ φ : ℕ → MvPolynomial (Fin 2 × ℕ) ℤ,
      ∀ ⦃R⦄ [CommRing R] (x y : 𝕎 R), (f x y).coeff = fun n => peval (φ n) ![x.coeff, y.coeff]
goal: ∃ φ,
  ∀ ⦃R : Type u_3⦄ [inst : CommRing R] (x y : WittVector p R),
    (f x y).coeff = fun n => WittVector.peval (φ n) ![x.coeff, y.coeff]

full_name: WittVector.IsPoly₂
code: /-- A binary function `f : Π R, 𝕎 R → 𝕎 R → 𝕎 R` on Witt vectors
is said to be *polynomial* if there is a family of polynomials `φₙ` over `ℤ` such that the `n`th
coefficient of `f x y` is given by evaluating `φₙ` at the coefficients of `x` and `y`.

See also `WittVector.IsPoly` for the unary variant.

The `ghost_calc` tactic makes use of the `IsPoly` and `IsPoly₂` typeclass and its instances.
(In Lean 3, there was an `@[is_poly]` attribute to manage these instances,
because typeclass resolution did not play well with function composition.
This no longer seems to be an issue, so that such instances can be defined directly.)
-/
class IsPoly₂ (f : ∀ ⦃R⦄ [CommRing R], WittVector p R → 𝕎 R → 𝕎 R) : Prop where mk' ::
  poly :
    ∃ φ : ℕ → MvPolynomial (Fin 2 × ℕ) ℤ,
      ∀ ⦃R⦄ [CommRing R] (x y : 𝕎 R), (f x y).coeff = fun n => peval (φ n) ![x.coeff, y.coeff]
goal: Prop

full_name: WittVector.IsPoly₂.comp
code: /-- The composition of polynomial functions is polynomial. -/
-- Porting note (#10754): made this an instance
instance IsPoly₂.comp {h f g} [hh : IsPoly₂ p h] [hf : IsPoly p f] [hg : IsPoly p g] :
    IsPoly₂ p fun R _Rcr x y => h (f x) (g y) 
goal: WittVector.IsPoly₂ p fun R _Rcr x y => h (f x) (g y)

full_name: WittVector.IsPoly.comp₂
code: /-- The composition of a polynomial function with a binary polynomial function is polynomial. -/
-- Porting note (#10754): made this an instance
instance IsPoly.comp₂ {g f} [hg : IsPoly p g] [hf : IsPoly₂ p f] :
    IsPoly₂ p fun R _Rcr x y => g (f x y) 
goal: WittVector.IsPoly₂ p fun R _Rcr x y => g (f x y)

full_name: WittVector.IsPoly₂.diag
code: /-- The diagonal `fun x ↦ f x x` of a polynomial function `f` is polynomial. -/
-- Porting note (#10754): made this an instance
instance IsPoly₂.diag {f} [hf : IsPoly₂ p f] : IsPoly p fun R _Rcr x => f x x 
goal: WittVector.IsPoly p fun R _Rcr x => f x x

full_name: WittVector.negIsPoly
code: /-- The additive negation is a polynomial function on Witt vectors. -/
-- Porting note: replaced `@[is_poly]` with `instance`.
instance negIsPoly [Fact p.Prime] : IsPoly p fun R _ => @Neg.neg (𝕎 R) _ 
goal: WittVector.IsPoly p fun R x => Neg.neg

full_name: WittVector.zeroIsPoly
code: /-- The function that is constantly zero on Witt vectors is a polynomial function. -/
instance zeroIsPoly [Fact p.Prime] : IsPoly p fun _ _ _ => 0 
goal: WittVector.IsPoly p fun x x_1 x_2 => 0

full_name: WittVector.bind₁_zero_wittPolynomial
code: @[simp]
theorem bind₁_zero_wittPolynomial [Fact p.Prime] (n : ℕ) :
    bind₁ (0 : ℕ → MvPolynomial ℕ R) (wittPolynomial p R n) = 0 
goal: (MvPolynomial.bind₁ 0) (wittPolynomial p R n) = 0

full_name: WittVector.onePoly
code: /-- The coefficients of `1 : 𝕎 R` as polynomials. -/
def onePoly (n : ℕ) : MvPolynomial ℕ ℤ 
goal: MvPolynomial ℕ ℤ

full_name: WittVector.bind₁_onePoly_wittPolynomial
code: @[simp]
theorem bind₁_onePoly_wittPolynomial [hp : Fact p.Prime] (n : ℕ) :
    bind₁ onePoly (wittPolynomial p ℤ n) = 1 
goal: (MvPolynomial.bind₁ WittVector.onePoly) (wittPolynomial p ℤ n) = 1

full_name: WittVector.oneIsPoly
code: /-- The function that is constantly one on Witt vectors is a polynomial function. -/
instance oneIsPoly [Fact p.Prime] : IsPoly p fun _ _ _ => 1 
goal: WittVector.IsPoly p fun x x_1 x_2 => 1

full_name: WittVector.addIsPoly₂
code: /-- Addition of Witt vectors is a polynomial function. -/
-- Porting note: replaced `@[is_poly]` with `instance`.
instance addIsPoly₂ [Fact p.Prime] : IsPoly₂ p fun _ _ => (· + ·) 
goal: WittVector.IsPoly₂ p fun x x_1 x_2 x_3 => x_2 + x_3

full_name: WittVector.mulIsPoly₂
code: /-- Multiplication of Witt vectors is a polynomial function. -/
-- Porting note: replaced `@[is_poly]` with `instance`.
instance mulIsPoly₂ [Fact p.Prime] : IsPoly₂ p fun _ _ => (· * ·) 
goal: WittVector.IsPoly₂ p fun x x_1 x_2 x_3 => x_2 * x_3

full_name: WittVector.IsPoly.map
code: theorem IsPoly.map [Fact p.Prime] {f} (hf : IsPoly p f) (g : R →+* S) (x : 𝕎 R) :
    map g (f x) = f (map g x) 
goal: (WittVector.map g) (f x) = f ((WittVector.map g) x)

full_name: WittVector.IsPoly₂.instInhabitedHAdd
code: instance [Fact p.Prime] : Inhabited (IsPoly₂ p (fun _ _ => (· + ·))) 
goal: Inhabited (WittVector.IsPoly₂ p fun x x_1 x_2 x_3 => x_2 + x_3)

full_name: WittVector.IsPoly₂.compLeft
code: /-- The composition of a binary polynomial function
 with a unary polynomial function in the first argument is polynomial. -/
theorem compLeft {g f} [IsPoly₂ p g] [IsPoly p f] :
    IsPoly₂ p fun _R _Rcr x y => g (f x) y 
goal: WittVector.IsPoly₂ p fun _R _Rcr x y => g (f x) y

full_name: WittVector.IsPoly₂.compRight
code: /-- The composition of a binary polynomial function
 with a unary polynomial function in the second argument is polynomial. -/
theorem compRight {g f} [IsPoly₂ p g] [IsPoly p f] :
    IsPoly₂ p fun _R _Rcr x y => g x (f y) 
goal: WittVector.IsPoly₂ p fun _R _Rcr x y => g x (f y)

full_name: WittVector.IsPoly₂.ext
code: theorem ext [Fact p.Prime] {f g} (hf : IsPoly₂ p f) (hg : IsPoly₂ p g)
    (h : ∀ (R : Type u) [_Rcr : CommRing R] (x y : 𝕎 R) (n : ℕ),
        ghostComponent n (f x y) = ghostComponent n (g x y)) :
    ∀ (R) [_Rcr : CommRing R] (x y : 𝕎 R), f x y = g x y 
goal: f x y = g x y

full_name: WittVector.IsPoly₂.map
code: theorem map [Fact p.Prime] {f} (hf : IsPoly₂ p f) (g : R →+* S) (x y : 𝕎 R) :
    map g (f x y) = f (map g x) (map g y) 
goal: (WittVector.map g) (f x y) = f ((WittVector.map g) x) ((WittVector.map g) y)

full_name: WittVector.Tactic.ghostSimp
code: /-- A macro for a common simplification when rewriting with ghost component equations. -/
syntax (name := ghostSimp) "ghost_simp" (simpArgs)? : tactic
goal: Lean.ParserDescr

full_name: WittVector.Tactic._aux_Mathlib_RingTheory_WittVector_IsPoly___macroRules_WittVector_Tactic_ghostSimp_1
code: macro_rules
  | `(tactic| ghost_simp $[[$simpArgs,*]]?) => do
    let args := simpArgs.map (·.getElems) |>.getD #[]
    `(tactic| simp only [← sub_eq_add_neg, ghost_simps, $args,*])
goal: Lean.Macro

full_name: WittVector.Tactic.ghostCalc
code: /-- `ghost_calc` is a tactic for proving identities between polynomial functions.
Typically, when faced with a goal like
```lean
∀ (x y : 𝕎 R), verschiebung (x * frobenius y) = verschiebung x * y
```
you can
1. call `ghost_calc`
2. do a small amount of manual work -- maybe nothing, maybe `rintro`, etc
3. call `ghost_simp`

and this will close the goal.

`ghost_calc` cannot detect whether you are dealing with unary or binary polynomial functions.
You must give it arguments to determine this.
If you are proving a universally quantified goal like the above,
call `ghost_calc _ _`.
If the variables are introduced already, call `ghost_calc x y`.
In the unary case, use `ghost_calc _` or `ghost_calc x`.

`ghost_calc` is a light wrapper around type class inference.
All it does is apply the appropriate extensionality lemma and try to infer the resulting goals.
This is subtle and Lean's elaborator doesn't like it because of the HO unification involved,
so it is easier (and prettier) to put it in a tactic script.
-/
syntax (name := ghostCalc) "ghost_calc" (ppSpace colGt term:max)* : tactic
goal: Lean.ParserDescr

full_name: _private.Mathlib.RingTheory.WittVector.IsPoly.0.WittVector.Tactic.runIntro
code: private def runIntro (ref : Syntax) (n : Name) : TacticM FVarId 
goal: Lean.Elab.Tactic.TacticM Lean.FVarId

full_name: _private.Mathlib.RingTheory.WittVector.IsPoly.0.WittVector.Tactic.getLocalOrIntro
code: private def getLocalOrIntro (t : Term) : TacticM FVarId 
goal: Lean.Elab.Tactic.TacticM Lean.FVarId

full_name: WittVector.Tactic._aux_Mathlib_RingTheory_WittVector_IsPoly___elabRules_WittVector_Tactic_ghostCalc_1
code: elab_rules : tactic | `(tactic| ghost_calc $[$ids']*) => do
  let ids ← ids'.mapM getLocalOrIntro
  withMainContext do
  let idsS ← ids.mapM (fun id => Elab.Term.exprToSyntax (.fvar id))
  let some (α, lhs, rhs) := (← getMainTarget'').eq?
    | throwError "ghost_calc expecting target to be an equality"
  let (``WittVector, #[_, R]) := α.getAppFnArgs
    | throwError "ghost_calc expecting target to be an equality of `WittVector`s"
  let instR ← Meta.synthInstance (← Meta.mkAppM ``CommRing #[R])
  unless instR.isFVar do
    throwError "{← Meta.inferType instR} instance is not local"
  let f ← Meta.mkLambdaFVars (#[R, instR] ++ ids.map .fvar) lhs
  let g ← Meta.mkLambdaFVars (#[R, instR] ++ ids.map .fvar) rhs
  let fS ← Elab.Term.exprToSyntax f
  let gS ← Elab.Term.exprToSyntax g
  match idsS with
    | #[x] => evalTactic (← `(tactic| refine IsPoly.ext (f := $fS) (g := $gS) ?_ ?_ ?_ _ $x))
    | #[x, y] => evalTactic (← `(tactic| refine IsPoly₂.ext (f := $fS) (g := $gS) ?_ ?_ ?_ _ $x $y))
    | _ => throwError "ghost_calc takes either one or two arguments"
  let nm ← withMainContext <|
    if let .fvar fvarId := (R : Expr) then
      fvarId.getUserName
    else
      Meta.getUnusedUserName `R
  evalTactic <| ← `(tactic| iterate 2 infer_instance)
  let R := mkIdent nm
  evalTactic <| ← `(tactic| clear! $R)
  evalTactic <| ← `(tactic| intro $(mkIdent nm):ident $(mkIdent (.str nm "_inst")):ident $ids'*)
goal: Lean.Elab.Tactic.Tactic