/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Yury Kudryashov

! This file was ported from Lean 3 source module algebra.algebra.prod
! leanprover-community/mathlib commit 23aa88e32dcc9d2a24cca7bc23268567ed4cd7d6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Algebra.Hom

/-!
# The R-algebra structure on products of R-algebras

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

The R-algebra structure on `Π i : I, A i` when each `A i` is an R-algebra.

## Main defintions

* `pi.algebra`
* `pi.eval_alg_hom`
* `pi.const_alg_hom`
-/

variable {R A B C : Type _}

variable [CommSemiring R]

variable [Semiring A] [Algebra R A] [Semiring B] [Algebra R B] [Semiring C] [Algebra R C]

namespace Prod

variable (R A B)

open Algebra

/- warning: prod.algebra -> Prod.algebra is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) (A : Type.{u2}) (B : Type.{u3}) [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4], Algebra.{u1, max u2 u3} R (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4)
but is expected to have type
  forall (R : Type.{u1}) (A : Type.{u2}) (B : Type.{u3}) [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4], Algebra.{u1, max u3 u2} R (Prod.{u2, u3} A B) _inst_1 (Prod.instSemiringProd.{u2, u3} A B _inst_2 _inst_4)
Case conversion may be inaccurate. Consider using '#align prod.algebra Prod.algebraₓ'. -/
instance algebra : Algebra R (A × B) :=
  { Prod.module,
    RingHom.prod (algebraMap R A)
      (algebraMap R
        B) with
    commutes' := by
      rintro r ⟨a, b⟩
      dsimp
      rw [commutes r a, commutes r b]
    smul_def' := by
      rintro r ⟨a, b⟩
      dsimp
      rw [Algebra.smul_def r a, Algebra.smul_def r b] }
#align prod.algebra Prod.algebra

variable {R A B}

/- warning: prod.algebra_map_apply -> Prod.algebraMap_apply is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4] (r : R), Eq.{max (succ u2) (succ u3)} (Prod.{u2, u3} A B) (coeFn.{max (succ u1) (succ (max u2 u3)), max (succ u1) (succ (max u2 u3))} (RingHom.{u1, max u2 u3} R (Prod.{u2, u3} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u2, u3} A B) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4))) (fun (_x : RingHom.{u1, max u2 u3} R (Prod.{u2, u3} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u2, u3} A B) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4))) => R -> (Prod.{u2, u3} A B)) (RingHom.hasCoeToFun.{u1, max u2 u3} R (Prod.{u2, u3} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u2, u3} A B) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4))) (algebraMap.{u1, max u2 u3} R (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) r) (Prod.mk.{u2, u3} A B (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} A _inst_2)) (fun (_x : RingHom.{u1, u2} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} A _inst_2)) => R -> A) (RingHom.hasCoeToFun.{u1, u2} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} A _inst_2)) (algebraMap.{u1, u2} R A _inst_1 _inst_2 _inst_3) r) (coeFn.{max (succ u1) (succ u3), max (succ u1) (succ u3)} (RingHom.{u1, u3} R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} B _inst_4)) (fun (_x : RingHom.{u1, u3} R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} B _inst_4)) => R -> B) (RingHom.hasCoeToFun.{u1, u3} R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} B _inst_4)) (algebraMap.{u1, u3} R B _inst_1 _inst_4 _inst_5) r))
but is expected to have type
  forall {R : Type.{u1}} {A : Type.{u3}} {B : Type.{u2}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u3} A] [_inst_3 : Algebra.{u1, u3} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u2} B] [_inst_5 : Algebra.{u1, u2} R B _inst_1 _inst_4] (r : R), Eq.{max (succ u3) (succ u2)} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Prod.{u3, u2} A B) r) (FunLike.coe.{max (max (succ u1) (succ u3)) (succ u2), succ u1, max (succ u3) (succ u2)} (RingHom.{u1, max u2 u3} R (Prod.{u3, u2} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u3, u2} A B) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Prod.{u3, u2} A B) _x) (MulHomClass.toFunLike.{max (max u1 u3) u2, u1, max u3 u2} (RingHom.{u1, max u2 u3} R (Prod.{u3, u2} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u3, u2} A B) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4))) R (Prod.{u3, u2} A B) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (NonUnitalNonAssocSemiring.toMul.{max u3 u2} (Prod.{u3, u2} A B) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u3 u2} (Prod.{u3, u2} A B) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u3, u2} A B) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4)))) (NonUnitalRingHomClass.toMulHomClass.{max (max u1 u3) u2, u1, max u3 u2} (RingHom.{u1, max u2 u3} R (Prod.{u3, u2} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u3, u2} A B) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4))) R (Prod.{u3, u2} A B) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u3 u2} (Prod.{u3, u2} A B) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u3, u2} A B) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4))) (RingHomClass.toNonUnitalRingHomClass.{max (max u1 u3) u2, u1, max u3 u2} (RingHom.{u1, max u2 u3} R (Prod.{u3, u2} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u3, u2} A B) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4))) R (Prod.{u3, u2} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u3, u2} A B) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4)) (RingHom.instRingHomClassRingHom.{u1, max u3 u2} R (Prod.{u3, u2} A B) (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{max u2 u3} (Prod.{u3, u2} A B) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4)))))) (algebraMap.{u1, max u2 u3} R (Prod.{u3, u2} A B) _inst_1 (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) r) (Prod.mk.{u3, u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) r) ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => B) r) (FunLike.coe.{max (succ u1) (succ u3), succ u1, succ u3} (RingHom.{u1, u3} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A _inst_2)) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) _x) (MulHomClass.toFunLike.{max u1 u3, u1, u3} (RingHom.{u1, u3} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A _inst_2)) R A (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (NonUnitalNonAssocSemiring.toMul.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (NonUnitalRingHomClass.toMulHomClass.{max u1 u3, u1, u3} (RingHom.{u1, u3} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A _inst_2)) R A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2)) (RingHomClass.toNonUnitalRingHomClass.{max u1 u3, u1, u3} (RingHom.{u1, u3} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A _inst_2)) R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A _inst_2) (RingHom.instRingHomClassRingHom.{u1, u3} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A _inst_2))))) (algebraMap.{u1, u3} R A _inst_1 _inst_2 _inst_3) r) (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (RingHom.{u1, u2} R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} B _inst_4)) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => B) _x) (MulHomClass.toFunLike.{max u1 u2, u1, u2} (RingHom.{u1, u2} R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} B _inst_4)) R B (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (NonUnitalNonAssocSemiring.toMul.{u2} B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4))) (NonUnitalRingHomClass.toMulHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} B _inst_4)) R B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4)) (RingHomClass.toNonUnitalRingHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} B _inst_4)) R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} B _inst_4) (RingHom.instRingHomClassRingHom.{u1, u2} R B (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} B _inst_4))))) (algebraMap.{u1, u2} R B _inst_1 _inst_4 _inst_5) r))
Case conversion may be inaccurate. Consider using '#align prod.algebra_map_apply Prod.algebraMap_applyₓ'. -/
@[simp]
theorem algebraMap_apply (r : R) : algebraMap R (A × B) r = (algebraMap R A r, algebraMap R B r) :=
  rfl
#align prod.algebra_map_apply Prod.algebraMap_apply

end Prod

namespace AlgHom

variable (R A B)

/- warning: alg_hom.fst -> AlgHom.fst is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) (A : Type.{u2}) (B : Type.{u3}) [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4], AlgHom.{u1, max u2 u3, u2} R (Prod.{u2, u3} A B) A _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) _inst_2 (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) _inst_3
but is expected to have type
  forall (R : Type.{u1}) (A : Type.{u2}) (B : Type.{u3}) [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4], AlgHom.{u1, max u3 u2, u2} R (Prod.{u2, u3} A B) A _inst_1 (Prod.instSemiringProd.{u2, u3} A B _inst_2 _inst_4) _inst_2 (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) _inst_3
Case conversion may be inaccurate. Consider using '#align alg_hom.fst AlgHom.fstₓ'. -/
/-- First projection as `alg_hom`. -/
def fst : A × B →ₐ[R] A :=
  { RingHom.fst A B with commutes' := fun r => rfl }
#align alg_hom.fst AlgHom.fst

/- warning: alg_hom.snd -> AlgHom.snd is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) (A : Type.{u2}) (B : Type.{u3}) [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4], AlgHom.{u1, max u2 u3, u3} R (Prod.{u2, u3} A B) B _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) _inst_4 (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) _inst_5
but is expected to have type
  forall (R : Type.{u1}) (A : Type.{u2}) (B : Type.{u3}) [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4], AlgHom.{u1, max u3 u2, u3} R (Prod.{u2, u3} A B) B _inst_1 (Prod.instSemiringProd.{u2, u3} A B _inst_2 _inst_4) _inst_4 (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) _inst_5
Case conversion may be inaccurate. Consider using '#align alg_hom.snd AlgHom.sndₓ'. -/
/-- Second projection as `alg_hom`. -/
def snd : A × B →ₐ[R] B :=
  { RingHom.snd A B with commutes' := fun r => rfl }
#align alg_hom.snd AlgHom.snd

variable {R A B}

/- warning: alg_hom.prod -> AlgHom.prod is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} {C : Type.{u4}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u4} C] [_inst_7 : Algebra.{u1, u4} R C _inst_1 _inst_6], (AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) -> (AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) -> (AlgHom.{u1, u2, max u3 u4} R A (Prod.{u3, u4} B C) _inst_1 _inst_2 (Prod.semiring.{u3, u4} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7))
but is expected to have type
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} {C : Type.{u4}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u4} C] [_inst_7 : Algebra.{u1, u4} R C _inst_1 _inst_6], (AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) -> (AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) -> (AlgHom.{u1, u2, max u4 u3} R A (Prod.{u3, u4} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u3, u4} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7))
Case conversion may be inaccurate. Consider using '#align alg_hom.prod AlgHom.prodₓ'. -/
/-- The `pi.prod` of two morphisms is a morphism. -/
@[simps]
def prod (f : A →ₐ[R] B) (g : A →ₐ[R] C) : A →ₐ[R] B × C :=
  { f.toRingHom.Prod g.toRingHom with
    commutes' := fun r => by
      simp only [to_ring_hom_eq_coe, RingHom.toFun_eq_coe, RingHom.prod_apply, coe_to_ring_hom,
        commutes, Prod.algebraMap_apply] }
#align alg_hom.prod AlgHom.prod

/- warning: alg_hom.coe_prod -> AlgHom.coe_prod is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} {C : Type.{u4}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u4} C] [_inst_7 : Algebra.{u1, u4} R C _inst_1 _inst_6] (f : AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (g : AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7), Eq.{max (succ u2) (succ (max u3 u4))} (A -> (Prod.{u3, u4} B C)) (coeFn.{max (succ u2) (succ (max u3 u4)), max (succ u2) (succ (max u3 u4))} (AlgHom.{u1, u2, max u3 u4} R A (Prod.{u3, u4} B C) _inst_1 _inst_2 (Prod.semiring.{u3, u4} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) (fun (_x : AlgHom.{u1, u2, max u3 u4} R A (Prod.{u3, u4} B C) _inst_1 _inst_2 (Prod.semiring.{u3, u4} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) => A -> (Prod.{u3, u4} B C)) ([anonymous].{u1, u2, max u3 u4} R A (Prod.{u3, u4} B C) _inst_1 _inst_2 (Prod.semiring.{u3, u4} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) (AlgHom.prod.{u1, u2, u3, u4} R A B C _inst_1 _inst_2 _inst_3 _inst_4 _inst_5 _inst_6 _inst_7 f g)) (Pi.prod.{u2, u3, u4} A (fun (ᾰ : A) => B) (fun (ᾰ : A) => C) (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (fun (_x : AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) => A -> B) ([anonymous].{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) f) (coeFn.{max (succ u2) (succ u4), max (succ u2) (succ u4)} (AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) (fun (_x : AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) => A -> C) ([anonymous].{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) g))
but is expected to have type
  forall {R : Type.{u4}} {A : Type.{u3}} {B : Type.{u2}} {C : Type.{u1}} [_inst_1 : CommSemiring.{u4} R] [_inst_2 : Semiring.{u3} A] [_inst_3 : Algebra.{u4, u3} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u2} B] [_inst_5 : Algebra.{u4, u2} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u1} C] [_inst_7 : Algebra.{u4, u1} R C _inst_1 _inst_6] (f : AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (g : AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7), Eq.{max (max (succ u3) (succ u2)) (succ u1)} (forall (ᾰ : A), (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : A) => Prod.{u2, u1} B C) ᾰ) (FunLike.coe.{max (max (succ u3) (succ u2)) (succ u1), succ u3, max (succ u2) (succ u1)} (AlgHom.{u4, u3, max u1 u2} R A (Prod.{u2, u1} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) A (fun (_x : A) => (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : A) => Prod.{u2, u1} B C) _x) (SMulHomClass.toFunLike.{max (max u3 u2) u1, u4, u3, max u2 u1} (AlgHom.{u4, u3, max u1 u2} R A (Prod.{u2, u1} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) R A (Prod.{u2, u1} B C) (SMulZeroClass.toSMul.{u4, u3} R A (AddMonoid.toZero.{u3} A (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))))) (DistribSMul.toSMulZeroClass.{u4, u3} R A (AddMonoid.toAddZeroClass.{u3} A (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))))) (DistribMulAction.toDistribSMul.{u4, u3} R A (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2)))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3))))) (SMulZeroClass.toSMul.{u4, max u2 u1} R (Prod.{u2, u1} B C) (AddMonoid.toZero.{max u2 u1} (Prod.{u2, u1} B C) (AddCommMonoid.toAddMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonUnitalNonAssocSemiring.toAddCommMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Semiring.toNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6)))))) (DistribSMul.toSMulZeroClass.{u4, max u2 u1} R (Prod.{u2, u1} B C) (AddMonoid.toAddZeroClass.{max u2 u1} (Prod.{u2, u1} B C) (AddCommMonoid.toAddMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonUnitalNonAssocSemiring.toAddCommMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Semiring.toNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6)))))) (DistribMulAction.toDistribSMul.{u4, max u2 u1} R (Prod.{u2, u1} B C) (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonUnitalNonAssocSemiring.toAddCommMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Semiring.toNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6))))) (Module.toDistribMulAction.{u4, max u2 u1} R (Prod.{u2, u1} B C) (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Semiring.toNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6)))) (Algebra.toModule.{u4, max u2 u1} R (Prod.{u2, u1} B C) _inst_1 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)))))) (DistribMulActionHomClass.toSMulHomClass.{max (max u3 u2) u1, u4, u3, max u2 u1} (AlgHom.{u4, u3, max u1 u2} R A (Prod.{u2, u1} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) R A (Prod.{u2, u1} B C) (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2)))) (AddCommMonoid.toAddMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonUnitalNonAssocSemiring.toAddCommMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Semiring.toNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6))))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3)) (Module.toDistribMulAction.{u4, max u2 u1} R (Prod.{u2, u1} B C) (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Semiring.toNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6)))) (Algebra.toModule.{u4, max u2 u1} R (Prod.{u2, u1} B C) _inst_1 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7))) (SemilinearMapClass.distribMulActionHomClass.{u4, u3, max u2 u1, max (max u3 u2) u1} R A (Prod.{u2, u1} B C) (AlgHom.{u4, u3, max u1 u2} R A (Prod.{u2, u1} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (NonUnitalNonAssocSemiring.toAddCommMonoid.{max u2 u1} (Prod.{u2, u1} B C) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Semiring.toNonAssocSemiring.{max u2 u1} (Prod.{u2, u1} B C) (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6)))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3) (Algebra.toModule.{u4, max u2 u1} R (Prod.{u2, u1} B C) _inst_1 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) (AlgHomClass.linearMapClass.{u4, u3, max u2 u1, max (max u3 u2) u1} R A (Prod.{u2, u1} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) (AlgHom.{u4, u3, max u1 u2} R A (Prod.{u2, u1} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)) (AlgHom.algHomClass.{u4, u3, max u2 u1} R A (Prod.{u2, u1} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7)))))) (AlgHom.prod.{u4, u3, u2, u1} R A B C _inst_1 _inst_2 _inst_3 _inst_4 _inst_5 _inst_6 _inst_7 f g)) (Pi.prod.{u3, u2, u1} A (fun (ᾰ : A) => (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : A) => B) ᾰ) (fun (ᾰ : A) => (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : A) => C) ᾰ) (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) A (fun (_x : A) => (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : A) => B) _x) (SMulHomClass.toFunLike.{max u3 u2, u4, u3, u2} (AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) R A B (SMulZeroClass.toSMul.{u4, u3} R A (AddMonoid.toZero.{u3} A (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))))) (DistribSMul.toSMulZeroClass.{u4, u3} R A (AddMonoid.toAddZeroClass.{u3} A (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))))) (DistribMulAction.toDistribSMul.{u4, u3} R A (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2)))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3))))) (SMulZeroClass.toSMul.{u4, u2} R B (AddMonoid.toZero.{u2} B (AddCommMonoid.toAddMonoid.{u2} B (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4))))) (DistribSMul.toSMulZeroClass.{u4, u2} R B (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4))))) (DistribMulAction.toDistribSMul.{u4, u2} R B (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u2} B (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4)))) (Module.toDistribMulAction.{u4, u2} R B (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4))) (Algebra.toModule.{u4, u2} R B _inst_1 _inst_4 _inst_5))))) (DistribMulActionHomClass.toSMulHomClass.{max u3 u2, u4, u3, u2} (AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) R A B (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2)))) (AddCommMonoid.toAddMonoid.{u2} B (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4)))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3)) (Module.toDistribMulAction.{u4, u2} R B (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4))) (Algebra.toModule.{u4, u2} R B _inst_1 _inst_4 _inst_5)) (SemilinearMapClass.distribMulActionHomClass.{u4, u3, u2, max u3 u2} R A B (AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} B (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} B (Semiring.toNonAssocSemiring.{u2} B _inst_4))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3) (Algebra.toModule.{u4, u2} R B _inst_1 _inst_4 _inst_5) (AlgHomClass.linearMapClass.{u4, u3, u2, max u3 u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5 (AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (AlgHom.algHomClass.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5))))) f) (FunLike.coe.{max (succ u3) (succ u1), succ u3, succ u1} (AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) A (fun (_x : A) => (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : A) => C) _x) (SMulHomClass.toFunLike.{max u3 u1, u4, u3, u1} (AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) R A C (SMulZeroClass.toSMul.{u4, u3} R A (AddMonoid.toZero.{u3} A (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))))) (DistribSMul.toSMulZeroClass.{u4, u3} R A (AddMonoid.toAddZeroClass.{u3} A (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))))) (DistribMulAction.toDistribSMul.{u4, u3} R A (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2)))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3))))) (SMulZeroClass.toSMul.{u4, u1} R C (AddMonoid.toZero.{u1} C (AddCommMonoid.toAddMonoid.{u1} C (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} C (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} C (Semiring.toNonAssocSemiring.{u1} C _inst_6))))) (DistribSMul.toSMulZeroClass.{u4, u1} R C (AddMonoid.toAddZeroClass.{u1} C (AddCommMonoid.toAddMonoid.{u1} C (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} C (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} C (Semiring.toNonAssocSemiring.{u1} C _inst_6))))) (DistribMulAction.toDistribSMul.{u4, u1} R C (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u1} C (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} C (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} C (Semiring.toNonAssocSemiring.{u1} C _inst_6)))) (Module.toDistribMulAction.{u4, u1} R C (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} C (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} C (Semiring.toNonAssocSemiring.{u1} C _inst_6))) (Algebra.toModule.{u4, u1} R C _inst_1 _inst_6 _inst_7))))) (DistribMulActionHomClass.toSMulHomClass.{max u3 u1, u4, u3, u1} (AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) R A C (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2)))) (AddCommMonoid.toAddMonoid.{u1} C (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} C (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} C (Semiring.toNonAssocSemiring.{u1} C _inst_6)))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3)) (Module.toDistribMulAction.{u4, u1} R C (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} C (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} C (Semiring.toNonAssocSemiring.{u1} C _inst_6))) (Algebra.toModule.{u4, u1} R C _inst_1 _inst_6 _inst_7)) (SemilinearMapClass.distribMulActionHomClass.{u4, u3, u1, max u3 u1} R A C (AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A _inst_2))) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} C (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} C (Semiring.toNonAssocSemiring.{u1} C _inst_6))) (Algebra.toModule.{u4, u3} R A _inst_1 _inst_2 _inst_3) (Algebra.toModule.{u4, u1} R C _inst_1 _inst_6 _inst_7) (AlgHomClass.linearMapClass.{u4, u3, u1, max u3 u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7 (AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) (AlgHom.algHomClass.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7))))) g))
Case conversion may be inaccurate. Consider using '#align alg_hom.coe_prod AlgHom.coe_prodₓ'. -/
theorem coe_prod (f : A →ₐ[R] B) (g : A →ₐ[R] C) : ⇑(f.Prod g) = Pi.prod f g :=
  rfl
#align alg_hom.coe_prod AlgHom.coe_prod

/- warning: alg_hom.fst_prod -> AlgHom.fst_prod is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} {C : Type.{u4}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u4} C] [_inst_7 : Algebra.{u1, u4} R C _inst_1 _inst_6] (f : AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (g : AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7), Eq.{max (succ u2) (succ u3)} (AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (AlgHom.comp.{u1, u2, max u3 u4, u3} R A (Prod.{u3, u4} B C) B _inst_1 _inst_2 (Prod.semiring.{u3, u4} B C _inst_4 _inst_6) _inst_4 _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) _inst_5 (AlgHom.fst.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) (AlgHom.prod.{u1, u2, u3, u4} R A B C _inst_1 _inst_2 _inst_3 _inst_4 _inst_5 _inst_6 _inst_7 f g)) f
but is expected to have type
  forall {R : Type.{u4}} {A : Type.{u3}} {B : Type.{u2}} {C : Type.{u1}} [_inst_1 : CommSemiring.{u4} R] [_inst_2 : Semiring.{u3} A] [_inst_3 : Algebra.{u4, u3} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u2} B] [_inst_5 : Algebra.{u4, u2} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u1} C] [_inst_7 : Algebra.{u4, u1} R C _inst_1 _inst_6] (f : AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (g : AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7), Eq.{max (succ u3) (succ u2)} (AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (AlgHom.comp.{u4, u3, max u2 u1, u2} R A (Prod.{u2, u1} B C) B _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_4 _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) _inst_5 (AlgHom.fst.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) (AlgHom.prod.{u4, u3, u2, u1} R A B C _inst_1 _inst_2 _inst_3 _inst_4 _inst_5 _inst_6 _inst_7 f g)) f
Case conversion may be inaccurate. Consider using '#align alg_hom.fst_prod AlgHom.fst_prodₓ'. -/
@[simp]
theorem fst_prod (f : A →ₐ[R] B) (g : A →ₐ[R] C) : (fst R B C).comp (prod f g) = f := by ext <;> rfl
#align alg_hom.fst_prod AlgHom.fst_prod

/- warning: alg_hom.snd_prod -> AlgHom.snd_prod is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} {C : Type.{u4}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u4} C] [_inst_7 : Algebra.{u1, u4} R C _inst_1 _inst_6] (f : AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (g : AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7), Eq.{max (succ u2) (succ u4)} (AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) (AlgHom.comp.{u1, u2, max u3 u4, u4} R A (Prod.{u3, u4} B C) C _inst_1 _inst_2 (Prod.semiring.{u3, u4} B C _inst_4 _inst_6) _inst_6 _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) _inst_7 (AlgHom.snd.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) (AlgHom.prod.{u1, u2, u3, u4} R A B C _inst_1 _inst_2 _inst_3 _inst_4 _inst_5 _inst_6 _inst_7 f g)) g
but is expected to have type
  forall {R : Type.{u4}} {A : Type.{u3}} {B : Type.{u2}} {C : Type.{u1}} [_inst_1 : CommSemiring.{u4} R] [_inst_2 : Semiring.{u3} A] [_inst_3 : Algebra.{u4, u3} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u2} B] [_inst_5 : Algebra.{u4, u2} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u1} C] [_inst_7 : Algebra.{u4, u1} R C _inst_1 _inst_6] (f : AlgHom.{u4, u3, u2} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (g : AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7), Eq.{max (succ u3) (succ u1)} (AlgHom.{u4, u3, u1} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7) (AlgHom.comp.{u4, u3, max u2 u1, u1} R A (Prod.{u2, u1} B C) C _inst_1 _inst_2 (Prod.instSemiringProd.{u2, u1} B C _inst_4 _inst_6) _inst_6 _inst_3 (Prod.algebra.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) _inst_7 (AlgHom.snd.{u4, u2, u1} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7) (AlgHom.prod.{u4, u3, u2, u1} R A B C _inst_1 _inst_2 _inst_3 _inst_4 _inst_5 _inst_6 _inst_7 f g)) g
Case conversion may be inaccurate. Consider using '#align alg_hom.snd_prod AlgHom.snd_prodₓ'. -/
@[simp]
theorem snd_prod (f : A →ₐ[R] B) (g : A →ₐ[R] C) : (snd R B C).comp (prod f g) = g := by ext <;> rfl
#align alg_hom.snd_prod AlgHom.snd_prod

/- warning: alg_hom.prod_fst_snd -> AlgHom.prod_fst_snd is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4], Eq.{succ (max u2 u3)} (AlgHom.{u1, max u2 u3, max u2 u3} R (Prod.{u2, u3} A B) (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (AlgHom.prod.{u1, max u2 u3, u2, u3} R (Prod.{u2, u3} A B) A B _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) _inst_2 _inst_3 _inst_4 _inst_5 (AlgHom.fst.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (AlgHom.snd.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (OfNat.ofNat.{max u2 u3} (AlgHom.{u1, max u2 u3, max u2 u3} R (Prod.{u2, u3} A B) (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) 1 (OfNat.mk.{max u2 u3} (AlgHom.{u1, max u2 u3, max u2 u3} R (Prod.{u2, u3} A B) (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) 1 (One.one.{max u2 u3} (AlgHom.{u1, max u2 u3, max u2 u3} R (Prod.{u2, u3} A B) (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (MulOneClass.toHasOne.{max u2 u3} (AlgHom.{u1, max u2 u3, max u2 u3} R (Prod.{u2, u3} A B) (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (Monoid.toMulOneClass.{max u2 u3} (AlgHom.{u1, max u2 u3, max u2 u3} R (Prod.{u2, u3} A B) (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (AlgHom.End.{u1, max u2 u3} R (Prod.{u2, u3} A B) _inst_1 (Prod.semiring.{u2, u3} A B _inst_2 _inst_4) (Prod.algebra.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)))))))
but is expected to have type
  forall {R : Type.{u1}} {A : Type.{u3}} {B : Type.{u2}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u3} A] [_inst_3 : Algebra.{u1, u3} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u2} B] [_inst_5 : Algebra.{u1, u2} R B _inst_1 _inst_4], Eq.{max (succ u3) (succ u2)} (AlgHom.{u1, max u3 u2, max u2 u3} R (Prod.{u3, u2} A B) (Prod.{u3, u2} A B) _inst_1 (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (AlgHom.prod.{u1, max u3 u2, u3, u2} R (Prod.{u3, u2} A B) A B _inst_1 (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) _inst_2 _inst_3 _inst_4 _inst_5 (AlgHom.fst.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (AlgHom.snd.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (OfNat.ofNat.{max u3 u2} (AlgHom.{u1, max u3 u2, max u2 u3} R (Prod.{u3, u2} A B) (Prod.{u3, u2} A B) _inst_1 (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) 1 (One.toOfNat1.{max u3 u2} (AlgHom.{u1, max u3 u2, max u2 u3} R (Prod.{u3, u2} A B) (Prod.{u3, u2} A B) _inst_1 (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (Monoid.toOne.{max u3 u2} (AlgHom.{u1, max u3 u2, max u2 u3} R (Prod.{u3, u2} A B) (Prod.{u3, u2} A B) _inst_1 (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (AlgHom.End.{u1, max u3 u2} R (Prod.{u3, u2} A B) _inst_1 (Prod.instSemiringProd.{u3, u2} A B _inst_2 _inst_4) (Prod.algebra.{u1, u3, u2} R A B _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)))))
Case conversion may be inaccurate. Consider using '#align alg_hom.prod_fst_snd AlgHom.prod_fst_sndₓ'. -/
@[simp]
theorem prod_fst_snd : prod (fst R A B) (snd R A B) = 1 :=
  FunLike.coe_injective Pi.prod_fst_snd
#align alg_hom.prod_fst_snd AlgHom.prod_fst_snd

/- warning: alg_hom.prod_equiv -> AlgHom.prodEquiv is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} {C : Type.{u4}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u4} C] [_inst_7 : Algebra.{u1, u4} R C _inst_1 _inst_6], Equiv.{max (succ (max u2 u3)) (succ (max u2 u4)), max (succ u2) (succ (max u3 u4))} (Prod.{max u2 u3, max u2 u4} (AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7)) (AlgHom.{u1, u2, max u3 u4} R A (Prod.{u3, u4} B C) _inst_1 _inst_2 (Prod.semiring.{u3, u4} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7))
but is expected to have type
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} {C : Type.{u4}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Semiring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 _inst_2] [_inst_4 : Semiring.{u3} B] [_inst_5 : Algebra.{u1, u3} R B _inst_1 _inst_4] [_inst_6 : Semiring.{u4} C] [_inst_7 : Algebra.{u1, u4} R C _inst_1 _inst_6], Equiv.{max (succ (max u4 u2)) (succ (max u3 u2)), max (succ (max u4 u3)) (succ u2)} (Prod.{max u3 u2, max u4 u2} (AlgHom.{u1, u2, u3} R A B _inst_1 _inst_2 _inst_4 _inst_3 _inst_5) (AlgHom.{u1, u2, u4} R A C _inst_1 _inst_2 _inst_6 _inst_3 _inst_7)) (AlgHom.{u1, u2, max u4 u3} R A (Prod.{u3, u4} B C) _inst_1 _inst_2 (Prod.instSemiringProd.{u3, u4} B C _inst_4 _inst_6) _inst_3 (Prod.algebra.{u1, u3, u4} R B C _inst_1 _inst_4 _inst_5 _inst_6 _inst_7))
Case conversion may be inaccurate. Consider using '#align alg_hom.prod_equiv AlgHom.prodEquivₓ'. -/
/-- Taking the product of two maps with the same domain is equivalent to taking the product of
their codomains. -/
@[simps]
def prodEquiv : (A →ₐ[R] B) × (A →ₐ[R] C) ≃ (A →ₐ[R] B × C)
    where
  toFun f := f.1.Prod f.2
  invFun f := ((fst _ _ _).comp f, (snd _ _ _).comp f)
  left_inv f := by ext <;> rfl
  right_inv f := by ext <;> rfl
#align alg_hom.prod_equiv AlgHom.prodEquiv

end AlgHom


import o_minimal.definable

-- Definability of order relations on definable types.

namespace o_minimal

open set

variables {R : Type*} (S : struc R)
variables {X : Type*} [has_coordinates R X] [is_definable S X]
variables {Y : Type*} [has_coordinates R Y] [is_definable S Y]

variables (X)

class is_definable_le [has_le X] : Prop :=
(definable_le' : def_set S {p : X × X | p.1 ≤ p.2})

variables {S X}

section has_le

variables [has_le X] [is_definable_le S X]

lemma definable_le' : def_set S {p : X × X | p.1 ≤ p.2} :=
is_definable_le.definable_le'

lemma definable_le {f : Y → X} (hf : def_fun S f) {g : Y → X} (hg : def_fun S g) :
  def_set S {p : Y | f p ≤ g p} :=
(hf.prod' hg).preimage definable_le'

end has_le

/-- In a partial order, `≤` is definable if `<` is. -/
lemma is_definable_le_of_definable_lt [partial_order X] (h : def_set S {p : X × X | p.1 < p.2}) :
  is_definable_le S X :=
begin
  constructor,
  simp_rw [le_iff_lt_or_eq],
  exact h.union def_set_diag
end

section preorder

variables [preorder X] [is_definable_le S X]

-- In a definable preorder, `<` is also definable.

lemma definable_lt' : def_set S {p : X × X | p.1 < p.2} :=
begin
  simp_rw [lt_iff_le_not_le],
  exact definable_le'.inter (definable_le def_fun.snd def_fun.fst).compl,
end

lemma definable_lt {f : Y → X} (hf : def_fun S f) {g : Y → X} (hg : def_fun S g) :
  def_set S {p : Y | f p < g p} :=
(hf.prod' hg).preimage definable_lt'

-- Intervals are definable.
-- For now we assume S has definable constants for simplicity.

variables [definable_constants S] (a b : X)

lemma def_set.Iio : def_set S (Iio a) := definable_lt def_fun.id def_fun_const
lemma def_set.Ioi : def_set S (Ioi a) := definable_lt def_fun_const def_fun.id
lemma def_set.Iic : def_set S (Iic a) := definable_le def_fun.id def_fun_const
lemma def_set.Ici : def_set S (Ici a) := definable_le def_fun_const def_fun.id

lemma def_set.Ioo : def_set S (Ioo a b) := (def_set.Ioi a).inter (def_set.Iio b)
lemma def_set.Ioc : def_set S (Ioc a b) := (def_set.Ioi a).inter (def_set.Iic b)
lemma def_set.Ico : def_set S (Ico a b) := (def_set.Ici a).inter (def_set.Iio b)
lemma def_set.Icc : def_set S (Icc a b) := (def_set.Ici a).inter (def_set.Iic b)

end preorder

end o_minimal


-- Copyright (c) 2017 Scott Morrison. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Stephen Morgan, Scott Morrison

open tactic
open smt_tactic

def pointwise_attribute : user_attribute := {
  name := `pointwise,
  descr := "A lemma that proves things are equal using the fact they are pointwise equal."
}

run_command attribute.register `pointwise_attribute

/- Try to apply one of the given lemas, it succeeds if one of them succeeds. -/
meta def any_apply : list name → tactic unit
| []      := failed
| (c::cs) := (mk_const c >>= fapply) <|> any_apply cs

meta def smt   : tactic unit := using_smt $ intros >> add_lemmas_from_facts >> try ematch >> try simp

meta def pointwise (and_then : tactic unit) : tactic unit :=
do cs ← attribute.get_instances `pointwise,
   try (any_apply cs >> and_then)

attribute [pointwise] funext

meta def blast : tactic unit := smt >> pointwise (repeat_at_most 2 blast) -- pointwise equality of functors creates two goals

notation `♮` := by abstract { blast }

@[pointwise] lemma {u v} pair_equality {α : Type u} {β : Type v} { X: α × β }: (X^.fst, X^.snd) = X := begin induction X, blast end
@[pointwise] lemma {u v} pair_equality_1 {α : Type u} {β : Type v} { X: α × β } { A : α } ( p : A = X^.fst ) : (A, X^.snd) = X := begin induction X, blast end
@[pointwise] lemma {u v} pair_equality_2 {α : Type u} {β : Type v} { X: α × β } { B : β } ( p : B = X^.snd ) : (X^.fst, B) = X := begin induction X, blast end
attribute [pointwise] subtype.eq

def {u} auto_cast {α β : Type u} {h : α = β} (a : α) := cast h a
@[simp] lemma {u} auto_cast_identity {α : Type u} (a : α) : @auto_cast α α ♮ a = a := ♮
notation `⟦` p `⟧` := @auto_cast _ _ ♮ p

universe variables u v u1 v1 u2 v2

structure Category :=
  (Obj : Type u)
  (Hom : Obj → Obj → Type v)
  (identity : Π X : Obj, Hom X X)
  (compose  : Π { X Y Z : Obj }, Hom X Y → Hom Y Z → Hom X Z)

  (left_identity  : ∀ { X Y : Obj } (f : Hom X Y), compose (identity _) f = f)
  (right_identity : ∀ { X Y : Obj } (f : Hom X Y), compose f (identity _) = f)
  (associativity  : ∀ { W X Y Z : Obj } (f : Hom W X) (g : Hom X Y) (h : Hom Y Z),
    compose (compose f g) h = compose f (compose g h))

attribute [simp] Category.left_identity
attribute [simp] Category.right_identity

structure Functor (C : Category.{ u1 v1 }) (D : Category.{ u2 v2 }) :=
  (onObjects   : C^.Obj → D^.Obj)
  (onMorphisms : Π { X Y : C^.Obj },
                C^.Hom X Y → D^.Hom (onObjects X) (onObjects Y))
  (identities : ∀ (X : C^.Obj),
    onMorphisms (C^.identity X) = D^.identity (onObjects X))
  (functoriality : ∀ { X Y Z : C^.Obj } (f : C^.Hom X Y) (g : C^.Hom Y Z),
    onMorphisms (C^.compose f g) = D^.compose (onMorphisms f) (onMorphisms g))

attribute [simp] Functor.identities
attribute [simp] Functor.functoriality

instance Functor_to_onObjects { C D : Category }: has_coe_to_fun (Functor C D) :=
{ F   := λ f, C^.Obj -> D^.Obj,
  coe := Functor.onObjects }

structure NaturalTransformation { C D : Category } ( F G : Functor C D ) :=
  (components: Π X : C^.Obj, D^.Hom (F X) (G X))
  (naturality: ∀ { X Y : C^.Obj } (f : C^.Hom X Y),
     D^.compose (F^.onMorphisms f) (components Y) = D^.compose (components X) (G^.onMorphisms f))

instance NaturalTransformation_to_components { C D : Category } { F G : Functor C D } : has_coe_to_fun (NaturalTransformation F G) :=
{ F   := λ f, Π X : C^.Obj, D^.Hom (F X) (G X),
  coe := NaturalTransformation.components }

@[reducible] definition vertical_composition_of_NaturalTransformations
  { C D : Category }
  { F G H : Functor C D }
  ( α : NaturalTransformation F G )
  ( β : NaturalTransformation G H ) : NaturalTransformation F H :=
  {
    components := λ X, D^.compose (α X) (β X),
    naturality := begin
                    blast,
                    begin[smt]
                      -- -- This doesn't work ("invalid expression"):
                      eblast_using [ D^.associativity, α^.naturality, β^.naturality ]
                      -- -- This does:
                      -- eblast_using [ Category.associativity, NaturalTransformation.naturality ]
                    end,
                  end
  }


/-
Copyright (c) 2020 Devon Tuma. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Devon Tuma
-/
import ring_theory.localization.away
import ring_theory.ideal.over
import ring_theory.jacobson_ideal

/-!
# Jacobson Rings
The following conditions are equivalent for a ring `R`:
1. Every radical ideal `I` is equal to its Jacobson radical
2. Every radical ideal `I` can be written as an intersection of maximal ideals
3. Every prime ideal `I` is equal to its Jacobson radical
Any ring satisfying any of these equivalent conditions is said to be Jacobson.
Some particular examples of Jacobson rings are also proven.
`is_jacobson_quotient` says that the quotient of a Jacobson ring is Jacobson.
`is_jacobson_localization` says the localization of a Jacobson ring to a single element is Jacobson.
`is_jacobson_polynomial_iff_is_jacobson` says polynomials over a Jacobson ring form a Jacobson ring.
## Main definitions
Let `R` be a commutative ring. Jacobson Rings are defined using the first of the above conditions
* `is_jacobson R` is the proposition that `R` is a Jacobson ring. It is a class,
  implemented as the predicate that for any ideal, `I.radical = I` implies `I.jacobson = I`.

## Main statements
* `is_jacobson_iff_prime_eq` is the equivalence between conditions 1 and 3 above.
* `is_jacobson_iff_Inf_maximal` is the equivalence between conditions 1 and 2 above.
* `is_jacobson_of_surjective` says that if `R` is a Jacobson ring and `f : R →+* S` is surjective,
  then `S` is also a Jacobson ring
* `is_jacobson_mv_polynomial` says that multi-variate polynomials over a Jacobson ring are Jacobson.
## Tags
Jacobson, Jacobson Ring
-/

namespace ideal

open polynomial
open_locale polynomial

section is_jacobson
variables {R S : Type*} [comm_ring R] [comm_ring S] {I : ideal R}

/-- A ring is a Jacobson ring if for every radical ideal `I`,
 the Jacobson radical of `I` is equal to `I`.
 See `is_jacobson_iff_prime_eq` and `is_jacobson_iff_Inf_maximal` for equivalent definitions. -/
class is_jacobson (R : Type*) [comm_ring R] : Prop :=
(out' : ∀ (I : ideal R), I.radical = I → I.jacobson = I)

theorem is_jacobson_iff {R} [comm_ring R] :
  is_jacobson R ↔ ∀ (I : ideal R), I.radical = I → I.jacobson = I :=
⟨λ h, h.1, λ h, ⟨h⟩⟩

theorem is_jacobson.out {R} [comm_ring R] :
  is_jacobson R → ∀ {I : ideal R}, I.radical = I → I.jacobson = I := is_jacobson_iff.1

/--  A ring is a Jacobson ring if and only if for all prime ideals `P`,
 the Jacobson radical of `P` is equal to `P`. -/
lemma is_jacobson_iff_prime_eq : is_jacobson R ↔ ∀ P : ideal R, is_prime P → P.jacobson = P :=
begin
  refine is_jacobson_iff.trans ⟨λ h I hI, h I (is_prime.radical hI), _⟩,
  refine λ h I hI, le_antisymm (λ x hx, _) (λ x hx, mem_Inf.mpr (λ _ hJ, hJ.left hx)),
  rw [← hI, radical_eq_Inf I, mem_Inf],
  intros P hP,
  rw set.mem_set_of_eq at hP,
  erw mem_Inf at hx,
  erw [← h P hP.right, mem_Inf],
  exact λ J hJ, hx ⟨le_trans hP.left hJ.left, hJ.right⟩
end

/-- A ring `R` is Jacobson if and only if for every prime ideal `I`,
 `I` can be written as the infimum of some collection of maximal ideals.
 Allowing ⊤ in the set `M` of maximal ideals is equivalent, but makes some proofs cleaner. -/
lemma is_jacobson_iff_Inf_maximal : is_jacobson R ↔
  ∀ {I : ideal R}, I.is_prime → ∃ M : set (ideal R), (∀ J ∈ M, is_maximal J ∨ J = ⊤) ∧ I = Inf M :=
⟨λ H I h, eq_jacobson_iff_Inf_maximal.1 (H.out (is_prime.radical h)),
  λ H, is_jacobson_iff_prime_eq.2 (λ P hP, eq_jacobson_iff_Inf_maximal.2 (H hP))⟩

lemma is_jacobson_iff_Inf_maximal' : is_jacobson R ↔
  ∀ {I : ideal R}, I.is_prime → ∃ M : set (ideal R),
  (∀ (J ∈ M) (K : ideal R), J < K → K = ⊤) ∧ I = Inf M :=
⟨λ H I h, eq_jacobson_iff_Inf_maximal'.1 (H.out (is_prime.radical h)),
  λ H, is_jacobson_iff_prime_eq.2 (λ P hP, eq_jacobson_iff_Inf_maximal'.2 (H hP))⟩

lemma radical_eq_jacobson [H : is_jacobson R] (I : ideal R) : I.radical = I.jacobson :=
le_antisymm (le_Inf (λ J ⟨hJ, hJ_max⟩, (is_prime.radical_le_iff hJ_max.is_prime).mpr hJ))
            ((H.out (radical_idem I)) ▸ (jacobson_mono le_radical))

/-- Fields have only two ideals, and the condition holds for both of them.  -/
@[priority 100]
instance is_jacobson_field {K : Type*} [field K] : is_jacobson K :=
⟨λ I hI, or.rec_on (eq_bot_or_top I)
(λ h, le_antisymm
  (Inf_le ⟨le_of_eq rfl, (eq.symm h) ▸ bot_is_maximal⟩)
  ((eq.symm h) ▸ bot_le))
(λ h, by rw [h, jacobson_eq_top_iff])⟩

theorem is_jacobson_of_surjective [H : is_jacobson R] :
  (∃ (f : R →+* S), function.surjective f) → is_jacobson S :=
begin
  rintros ⟨f, hf⟩,
  rw is_jacobson_iff_Inf_maximal,
  intros p hp,
  use map f '' {J : ideal R | comap f p ≤ J ∧ J.is_maximal },
  use λ j ⟨J, hJ, hmap⟩, hmap ▸ or.symm (map_eq_top_or_is_maximal_of_surjective f hf hJ.right),
  have : p = map f ((comap f p).jacobson),
  from (is_jacobson.out' (comap f p) (by rw [← comap_radical, is_prime.radical hp])).symm
    ▸ (map_comap_of_surjective f hf p).symm,
  exact eq.trans this (map_Inf hf (λ J ⟨hJ, _⟩, le_trans (ideal.ker_le_comap f) hJ)),
end

@[priority 100]
instance is_jacobson_quotient [is_jacobson R] : is_jacobson (R ⧸ I) :=
is_jacobson_of_surjective ⟨quotient.mk I, (by rintro ⟨x⟩; use x; refl)⟩

lemma is_jacobson_iso (e : R ≃+* S) : is_jacobson R ↔ is_jacobson S :=
⟨λ h, @is_jacobson_of_surjective _ _ _ _ h ⟨(e : R →+* S), e.surjective⟩,
  λ h, @is_jacobson_of_surjective _ _ _ _ h ⟨(e.symm : S →+* R), e.symm.surjective⟩⟩

lemma is_jacobson_of_is_integral [algebra R S] (hRS : algebra.is_integral R S)
  (hR : is_jacobson R) : is_jacobson S :=
begin
  rw is_jacobson_iff_prime_eq,
  introsI P hP,
  by_cases hP_top : comap (algebra_map R S) P = ⊤,
  { simp [comap_eq_top_iff.1 hP_top] },
  { haveI : nontrivial (R ⧸ comap (algebra_map R S) P) := quotient.nontrivial hP_top,
    rw jacobson_eq_iff_jacobson_quotient_eq_bot,
    refine eq_bot_of_comap_eq_bot (is_integral_quotient_of_is_integral hRS) _,
    rw [eq_bot_iff, ← jacobson_eq_iff_jacobson_quotient_eq_bot.1 ((is_jacobson_iff_prime_eq.1 hR)
      (comap (algebra_map R S) P) (comap_is_prime _ _)), comap_jacobson],
    refine Inf_le_Inf (λ J hJ, _),
    simp only [true_and, set.mem_image, bot_le, set.mem_set_of_eq],
    haveI : J.is_maximal, { simpa using hJ },
    exact exists_ideal_over_maximal_of_is_integral (is_integral_quotient_of_is_integral hRS) J
      (comap_bot_le_of_injective _ algebra_map_quotient_injective) }
end

lemma is_jacobson_of_is_integral' (f : R →+* S) (hf : f.is_integral)
  (hR : is_jacobson R) : is_jacobson S :=
@is_jacobson_of_is_integral _ _ _ _ f.to_algebra hf hR

end is_jacobson

section localization
open is_localization submonoid
variables {R S : Type*} [comm_ring R] [comm_ring S] {I : ideal R}
variables (y : R) [algebra R S] [is_localization.away y S]

lemma disjoint_powers_iff_not_mem (hI : I.radical = I) :
  disjoint ((submonoid.powers y) : set R) ↑I ↔ y ∉ I.1 :=
begin
  refine ⟨λ h, set.disjoint_left.1 h (mem_powers _), λ h, (disjoint_iff).mpr (eq_bot_iff.mpr _)⟩,
  rintros x ⟨⟨n, rfl⟩, hx'⟩,
  rw [← hI] at hx',
  exact absurd (hI ▸ mem_radical_of_pow_mem hx' : y ∈ I.carrier) h
end

variables (S)

/-- If `R` is a Jacobson ring, then maximal ideals in the localization at `y`
correspond to maximal ideals in the original ring `R` that don't contain `y`.
This lemma gives the correspondence in the particular case of an ideal and its comap.
See `le_rel_iso_of_maximal` for the more general relation isomorphism -/
lemma is_maximal_iff_is_maximal_disjoint [H : is_jacobson R] (J : ideal S) :
  J.is_maximal ↔ (comap (algebra_map R S) J).is_maximal ∧ y ∉ ideal.comap (algebra_map R S) J :=
begin
  split,
  { refine λ h, ⟨_, λ hy, h.ne_top (ideal.eq_top_of_is_unit_mem _ hy
      (map_units _ ⟨y, submonoid.mem_powers _⟩))⟩,
    have hJ : J.is_prime := is_maximal.is_prime h,
    rw is_prime_iff_is_prime_disjoint (submonoid.powers y) at hJ,
    have : y ∉ (comap (algebra_map R S) J).1 :=
      set.disjoint_left.1 hJ.right (submonoid.mem_powers _),
    erw [← H.out (is_prime.radical hJ.left), mem_Inf] at this,
    push_neg at this,
    rcases this with ⟨I, hI, hI'⟩,
    convert hI.right,
    by_cases hJ : J = map (algebra_map R S) I,
    { rw [hJ, comap_map_of_is_prime_disjoint (powers y) S I (is_maximal.is_prime hI.right)],
      rwa disjoint_powers_iff_not_mem y (is_maximal.is_prime hI.right).radical },
    { have hI_p : (map (algebra_map R S) I).is_prime,
      { refine is_prime_of_is_prime_disjoint (powers y) _ I hI.right.is_prime _,
        rwa disjoint_powers_iff_not_mem y (is_maximal.is_prime hI.right).radical },
      have : J ≤ map (algebra_map R S) I :=
        (map_comap (submonoid.powers y) S J) ▸ (map_mono hI.left),
      exact absurd (h.1.2 _ (lt_of_le_of_ne this hJ)) hI_p.1 } },
  { refine λ h, ⟨⟨λ hJ, h.1.ne_top (eq_top_iff.2 _), λ I hI, _⟩⟩,
    { rwa [eq_top_iff, ← (is_localization.order_embedding (powers y) S).le_iff_le] at hJ },
    { have := congr_arg (map (algebra_map R S)) (h.1.1.2 _ ⟨comap_mono (le_of_lt hI), _⟩),
      rwa [map_comap (powers y) S I, map_top] at this,
      refine λ hI', hI.right _,
      rw [← map_comap (powers y) S I, ← map_comap (powers y) S J],
      exact map_mono hI' } }
end

variables {S}

/-- If `R` is a Jacobson ring, then maximal ideals in the localization at `y`
correspond to maximal ideals in the original ring `R` that don't contain `y`.
This lemma gives the correspondence in the particular case of an ideal and its map.
See `le_rel_iso_of_maximal` for the more general statement, and the reverse of this implication -/
lemma is_maximal_of_is_maximal_disjoint [is_jacobson R] (I : ideal R) (hI : I.is_maximal)
  (hy : y ∉ I) : (map (algebra_map R S) I).is_maximal :=
begin
  rw [is_maximal_iff_is_maximal_disjoint S y,
    comap_map_of_is_prime_disjoint (powers y) S I (is_maximal.is_prime hI)
    ((disjoint_powers_iff_not_mem y (is_maximal.is_prime hI).radical).2 hy)],
  exact ⟨hI, hy⟩
end

/-- If `R` is a Jacobson ring, then maximal ideals in the localization at `y`
correspond to maximal ideals in the original ring `R` that don't contain `y` -/
def order_iso_of_maximal [is_jacobson R] :
  {p : ideal S // p.is_maximal} ≃o {p : ideal R // p.is_maximal ∧ y ∉ p} :=
{ to_fun := λ p,
    ⟨ideal.comap (algebra_map R S) p.1, (is_maximal_iff_is_maximal_disjoint S y p.1).1 p.2⟩,
  inv_fun := λ p,
    ⟨ideal.map (algebra_map R S) p.1, is_maximal_of_is_maximal_disjoint y p.1 p.2.1 p.2.2⟩,
  left_inv := λ J, subtype.eq (map_comap (powers y) S J),
  right_inv := λ I, subtype.eq (comap_map_of_is_prime_disjoint _ _ I.1 (is_maximal.is_prime I.2.1)
    ((disjoint_powers_iff_not_mem y I.2.1.is_prime.radical).2 I.2.2)),
  map_rel_iff' := λ I I', ⟨λ h, (show I.val ≤ I'.val,
    from (map_comap (powers y) S I.val) ▸ (map_comap (powers y) S I'.val) ▸ (ideal.map_mono h)),
    λ h x hx, h hx⟩ }

include y

/-- If `S` is the localization of the Jacobson ring `R` at the submonoid generated by `y : R`, then
`S` is Jacobson. -/
lemma is_jacobson_localization [H : is_jacobson R] : is_jacobson S :=
begin
  rw is_jacobson_iff_prime_eq,
  refine λ P' hP', le_antisymm _ le_jacobson,
  obtain ⟨hP', hPM⟩ := (is_localization.is_prime_iff_is_prime_disjoint (powers y) S P').mp hP',
  have hP := H.out (is_prime.radical hP'),
  refine (le_of_eq (is_localization.map_comap (powers y) S P'.jacobson).symm).trans
    ((map_mono _).trans (le_of_eq (is_localization.map_comap (powers y) S P'))),
  have : Inf { I : ideal R | comap (algebra_map R S) P' ≤ I ∧ I.is_maximal ∧ y ∉ I } ≤
    comap (algebra_map R S) P',
  { intros x hx,
    have hxy : x * y ∈ (comap (algebra_map R S) P').jacobson,
    { rw [ideal.jacobson, mem_Inf],
      intros J hJ,
      by_cases y ∈ J,
      { exact J.mul_mem_left x h },
      { exact J.mul_mem_right y ((mem_Inf.1 hx) ⟨hJ.left, ⟨hJ.right, h⟩⟩) } },
    rw hP at hxy,
    cases hP'.mem_or_mem hxy with hxy hxy,
    { exact hxy },
    { exact (hPM ⟨submonoid.mem_powers _, hxy⟩).elim } },
  refine le_trans _ this,
  rw [ideal.jacobson, comap_Inf', Inf_eq_infi],
  refine infi_le_infi_of_subset (λ I hI, ⟨map (algebra_map R S) I, ⟨_, _⟩⟩),
  { exact ⟨le_trans (le_of_eq ((is_localization.map_comap (powers y) S P').symm)) (map_mono hI.1),
    is_maximal_of_is_maximal_disjoint y _ hI.2.1 hI.2.2⟩ },
  { exact is_localization.comap_map_of_is_prime_disjoint _ S I (is_maximal.is_prime hI.2.1)
    ((disjoint_powers_iff_not_mem y hI.2.1.is_prime.radical).2 hI.2.2) }
end

end localization

namespace polynomial
open polynomial

section comm_ring
variables {R S : Type*} [comm_ring R] [comm_ring S] [is_domain S]
variables {Rₘ Sₘ : Type*} [comm_ring Rₘ] [comm_ring Sₘ]

/-- If `I` is a prime ideal of `polynomial R` and `pX ∈ I` is a non-constant polynomial,
  then the map `R →+* R[x]/I` descends to an integral map when localizing at `pX.leading_coeff`.
  In particular `X` is integral because it satisfies `pX`, and constants are trivially integral,
  so integrality of the entire extension follows by closure under addition and multiplication. -/
lemma is_integral_is_localization_polynomial_quotient
  (P : ideal R[X]) (pX : R[X]) (hpX : pX ∈ P)
  [algebra (R ⧸ P.comap (C : R →+* _)) Rₘ]
  [is_localization.away (pX.map (quotient.mk (P.comap C))).leading_coeff Rₘ]
  [algebra (R[X] ⧸ P) Sₘ]
  [is_localization ((submonoid.powers (pX.map (quotient.mk (P.comap C))).leading_coeff).map
    (quotient_map P C le_rfl) : submonoid (R[X] ⧸ P)) Sₘ] :
  (is_localization.map Sₘ (quotient_map P C le_rfl)
    ((submonoid.powers (pX.map (quotient.mk (P.comap C))).leading_coeff).le_comap_map) : Rₘ →+* _)
    .is_integral :=
begin
  let P' : ideal R := P.comap C,
  let M : submonoid (R ⧸ P') :=
  submonoid.powers (pX.map (quotient.mk (P.comap C))).leading_coeff,
  let M' : submonoid (R[X] ⧸ P) :=
  (submonoid.powers (pX.map (quotient.mk (P.comap C))).leading_coeff).map (quotient_map P C le_rfl),
  let φ : R ⧸ P' →+* R[X] ⧸ P := quotient_map P C le_rfl,
  let φ' := is_localization.map Sₘ φ M.le_comap_map,
  have hφ' : φ.comp (quotient.mk P') = (quotient.mk P).comp C := rfl,
  intro p,
  obtain ⟨⟨p', ⟨q, hq⟩⟩, hp⟩ := is_localization.surj M' p,
  suffices : φ'.is_integral_elem (algebra_map _ _ p'),
  { obtain ⟨q', hq', rfl⟩ := hq,
    obtain ⟨q'', hq''⟩ := is_unit_iff_exists_inv'.1 (is_localization.map_units Rₘ (⟨q', hq'⟩ : M)),
    refine φ'.is_integral_of_is_integral_mul_unit p (algebra_map _ _ (φ q')) q'' _ (hp.symm ▸ this),
    convert trans (trans (φ'.map_mul _ _).symm (congr_arg φ' hq'')) φ'.map_one using 2,
    rw [← φ'.comp_apply, is_localization.map_comp, ring_hom.comp_apply, subtype.coe_mk] },
  refine is_integral_of_mem_closure''
    (((algebra_map _ Sₘ).comp (quotient.mk P)) '' (insert X {p | p.degree ≤ 0})) _ _ _,
  { rintros x ⟨p, hp, rfl⟩,
    refine hp.rec_on (λ hy, _) (λ hy, _),
    { refine hy.symm ▸ (φ.is_integral_elem_localization_at_leading_coeff ((quotient.mk P) X)
        (pX.map (quotient.mk P')) _ M ⟨1, pow_one _⟩),
      rwa [eval₂_map, hφ', ← hom_eval₂, quotient.eq_zero_iff_mem, eval₂_C_X] },
    { rw [set.mem_set_of_eq, degree_le_zero_iff] at hy,
      refine hy.symm ▸ ⟨X - C (algebra_map _ _ ((quotient.mk P') (p.coeff 0))), monic_X_sub_C _, _⟩,
      simp only [eval₂_sub, eval₂_C, eval₂_X],
      rw [sub_eq_zero, ← φ'.comp_apply, is_localization.map_comp],
      refl } },
  { obtain ⟨p, rfl⟩ := quotient.mk_surjective p',
    refine polynomial.induction_on p
      (λ r, subring.subset_closure $ set.mem_image_of_mem _ (or.inr degree_C_le))
      (λ _ _ h1 h2, _) (λ n _ hr, _),
    { convert subring.add_mem _ h1 h2,
      rw [ring_hom.map_add, ring_hom.map_add] },
    { rw [pow_succ X n, mul_comm X, ← mul_assoc, ring_hom.map_mul, ring_hom.map_mul],
      exact subring.mul_mem _ hr (subring.subset_closure (set.mem_image_of_mem _ (or.inl rfl))) } },
end

/-- If `f : R → S` descends to an integral map in the localization at `x`,
  and `R` is a Jacobson ring, then the intersection of all maximal ideals in `S` is trivial -/
lemma jacobson_bot_of_integral_localization
  {R : Type*} [comm_ring R] [is_domain R] [is_jacobson R]
  (Rₘ Sₘ : Type*) [comm_ring Rₘ] [comm_ring Sₘ]
  (φ : R →+* S) (hφ : function.injective φ) (x : R) (hx : x ≠ 0)
  [algebra R Rₘ] [is_localization.away x Rₘ]
  [algebra S Sₘ] [is_localization ((submonoid.powers x).map φ : submonoid S) Sₘ]
  (hφ' : ring_hom.is_integral
    (is_localization.map Sₘ φ (submonoid.powers x).le_comap_map : Rₘ →+* Sₘ)) :
  (⊥ : ideal S).jacobson = (⊥ : ideal S) :=
begin
  have hM : ((submonoid.powers x).map φ : submonoid S) ≤ non_zero_divisors S :=
    map_le_non_zero_divisors_of_injective φ hφ (powers_le_non_zero_divisors_of_no_zero_divisors hx),
  letI : is_domain Sₘ := is_localization.is_domain_of_le_non_zero_divisors _ hM,
  let φ' : Rₘ →+* Sₘ := is_localization.map _ φ (submonoid.powers x).le_comap_map,
  suffices : ∀ I : ideal Sₘ, I.is_maximal → (I.comap (algebra_map S Sₘ)).is_maximal,
  { have hϕ' : comap (algebra_map S Sₘ) (⊥ : ideal Sₘ) = (⊥ : ideal S),
    { rw [← ring_hom.ker_eq_comap_bot, ← ring_hom.injective_iff_ker_eq_bot],
      exact is_localization.injective Sₘ hM },
    have hSₘ : is_jacobson Sₘ := is_jacobson_of_is_integral' φ' hφ' (is_jacobson_localization x),
    refine eq_bot_iff.mpr (le_trans _ (le_of_eq hϕ')),
    rw [← hSₘ.out radical_bot_of_is_domain, comap_jacobson],
    exact Inf_le_Inf (λ j hj, ⟨bot_le, let ⟨J, hJ⟩ := hj in hJ.2 ▸ this J hJ.1.2⟩) },
  introsI I hI,
  -- Remainder of the proof is pulling and pushing ideals around the square and the quotient square
  haveI : (I.comap (algebra_map S Sₘ)).is_prime := comap_is_prime _ I,
  haveI : (I.comap φ').is_prime := comap_is_prime φ' I,
  haveI : (⊥ : ideal (S ⧸ I.comap (algebra_map S Sₘ))).is_prime := bot_prime,
  have hcomm: φ'.comp (algebra_map R Rₘ) = (algebra_map S Sₘ).comp φ := is_localization.map_comp _,
  let f := quotient_map (I.comap (algebra_map S Sₘ)) φ le_rfl,
  let g := quotient_map I (algebra_map S Sₘ) le_rfl,
  have := is_maximal_comap_of_is_integral_of_is_maximal' φ' hφ' I
    (by convert hI; casesI _inst_4; refl),
  have := ((is_maximal_iff_is_maximal_disjoint Rₘ x _).1 this).left,
  have : ((I.comap (algebra_map S Sₘ)).comap φ).is_maximal,
  { rwa [comap_comap, hcomm, ← comap_comap] at this },
  rw ← bot_quotient_is_maximal_iff at this ⊢,
  refine is_maximal_of_is_integral_of_is_maximal_comap' f _ ⊥
    ((eq_bot_iff.2 (comap_bot_le_of_injective f quotient_map_injective)).symm ▸ this),
  exact f.is_integral_tower_bot_of_is_integral g quotient_map_injective
    ((comp_quotient_map_eq_of_comp_eq hcomm I).symm ▸
    (ring_hom.is_integral_trans _ _ (ring_hom.is_integral_of_surjective _
      (is_localization.surjective_quotient_map_of_maximal_of_localization (submonoid.powers x) Rₘ
      (by rwa [comap_comap, hcomm, ← bot_quotient_is_maximal_iff])))
      (ring_hom.is_integral_quotient_of_is_integral _ hφ'))),
end

/-- Used to bootstrap the proof of `is_jacobson_polynomial_iff_is_jacobson`.
  That theorem is more general and should be used instead of this one. -/
private lemma is_jacobson_polynomial_of_domain
  (R : Type*) [comm_ring R] [is_domain R] [hR : is_jacobson R]
  (P : ideal R[X]) [is_prime P] (hP : ∀ (x : R), C x ∈ P → x = 0) :
  P.jacobson = P :=
begin
  by_cases Pb : P = ⊥,
  { exact Pb.symm ▸ jacobson_bot_polynomial_of_jacobson_bot
      (hR.out radical_bot_of_is_domain) },
  { rw jacobson_eq_iff_jacobson_quotient_eq_bot,
    haveI : (P.comap (C : R →+* R[X])).is_prime := comap_is_prime C P,
    obtain ⟨p, pP, p0⟩ := exists_nonzero_mem_of_ne_bot Pb hP,
    let x := (polynomial.map (quotient.mk (comap (C : R →+* _) P)) p).leading_coeff,
    have hx : x ≠ 0 := by rwa [ne.def, leading_coeff_eq_zero],
    refine jacobson_bot_of_integral_localization
      (localization.away x)
      (localization ((submonoid.powers x).map (P.quotient_map C le_rfl) :
        submonoid (R[X] ⧸ P)))
      (quotient_map P C le_rfl) quotient_map_injective
      x hx
      _,
    -- `convert` is noticeably faster than `exact` here:
    convert is_integral_is_localization_polynomial_quotient P p pP }
end

lemma is_jacobson_polynomial_of_is_jacobson (hR : is_jacobson R) :
  is_jacobson R[X] :=
begin
  refine is_jacobson_iff_prime_eq.mpr (λ I, _),
  introI hI,
  let R' : subring (R[X] ⧸ I) := ((quotient.mk I).comp C).range,
  let i : R →+* R' := ((quotient.mk I).comp C).range_restrict,
  have hi : function.surjective (i : R → R') := ((quotient.mk I).comp C).range_restrict_surjective,
  have hi' : (polynomial.map_ring_hom i : R[X] →+* R'[X]).ker ≤ I,
  { refine λ f hf, polynomial_mem_ideal_of_coeff_mem_ideal I f (λ n, _),
    replace hf := congr_arg (λ (g : polynomial (((quotient.mk I).comp C).range)), g.coeff n) hf,
    change (polynomial.map ((quotient.mk I).comp C).range_restrict f).coeff n = 0 at hf,
    rw [coeff_map, subtype.ext_iff] at hf,
    rwa [mem_comap, ← quotient.eq_zero_iff_mem, ← ring_hom.comp_apply], },
  haveI := map_is_prime_of_surjective
    (show function.surjective (map_ring_hom i), from map_surjective i hi) hi',
  suffices : (I.map (polynomial.map_ring_hom i)).jacobson = (I.map (polynomial.map_ring_hom i)),
  { replace this := congr_arg (comap (polynomial.map_ring_hom i)) this,
    rw [← map_jacobson_of_surjective _ hi',
      comap_map_of_surjective _ _, comap_map_of_surjective _ _] at this,
    refine le_antisymm (le_trans (le_sup_of_le_left le_rfl)
      (le_trans (le_of_eq this) (sup_le le_rfl hi'))) le_jacobson,
    all_goals {exact polynomial.map_surjective i hi} },
  exact @is_jacobson_polynomial_of_domain R' _ _ (is_jacobson_of_surjective ⟨i, hi⟩)
    (map (map_ring_hom i) I) _ (eq_zero_of_polynomial_mem_map_range I),
end

theorem is_jacobson_polynomial_iff_is_jacobson :
  is_jacobson R[X] ↔ is_jacobson R :=
begin
  refine ⟨_, is_jacobson_polynomial_of_is_jacobson⟩,
  introI H,
  exact is_jacobson_of_surjective ⟨eval₂_ring_hom (ring_hom.id _) 1, λ x,
    ⟨C x, by simp only [coe_eval₂_ring_hom, ring_hom.id_apply, eval₂_C]⟩⟩,
end

instance [is_jacobson R] : is_jacobson R[X] :=
is_jacobson_polynomial_iff_is_jacobson.mpr ‹is_jacobson R›

end comm_ring

section
variables {R : Type*} [comm_ring R] [is_jacobson R]
variables (P : ideal R[X]) [hP : P.is_maximal]

include P hP

lemma is_maximal_comap_C_of_is_maximal [nontrivial R] (hP' : ∀ (x : R), C x ∈ P → x = 0) :
  is_maximal (comap C P : ideal R) :=
begin
  haveI hp'_prime : (P.comap C : ideal R).is_prime := comap_is_prime C P,
  obtain ⟨m, hm⟩ := submodule.nonzero_mem_of_bot_lt (bot_lt_of_maximal P polynomial_not_is_field),
  have : (m : R[X]) ≠ 0, rwa [ne.def, submodule.coe_eq_zero],
  let φ : R ⧸ P.comap C →+* R[X] ⧸ P := quotient_map P C le_rfl,
  let M : submonoid (R ⧸ P.comap C) :=
    submonoid.powers ((m : R[X]).map (quotient.mk (P.comap C : ideal R))).leading_coeff,
  rw ← bot_quotient_is_maximal_iff,
  have hp0 : ((m : R[X]).map (quotient.mk (P.comap C : ideal R))).leading_coeff ≠ 0 :=
    λ hp0', this $ map_injective (quotient.mk (P.comap C : ideal R))
      ((quotient.mk (P.comap C : ideal R)).injective_iff.2 (λ x hx,
      by rwa [quotient.eq_zero_iff_mem, (by rwa eq_bot_iff : (P.comap C : ideal R) = ⊥)] at hx))
      (by simpa only [leading_coeff_eq_zero, polynomial.map_zero] using hp0'),
  have hM : (0 : R ⧸ P.comap C) ∉ M := λ ⟨n, hn⟩, hp0 (pow_eq_zero hn),
  suffices : (⊥ : ideal (localization M)).is_maximal,
  { rw ← is_localization.comap_map_of_is_prime_disjoint M (localization M) ⊥ bot_prime
        (λ x hx, hM (hx.2 ▸ hx.1)),
    refine ((is_maximal_iff_is_maximal_disjoint (localization M) _ _).mp (by rwa map_bot)).1,
    swap, exact localization.is_localization },
  let M' : submonoid (R[X] ⧸ P) := M.map φ,
  have hM' : (0 : R[X] ⧸ P) ∉ M' :=
    λ ⟨z, hz⟩, hM (quotient_map_injective (trans hz.2 φ.map_zero.symm) ▸ hz.1),
  haveI : is_domain (localization M') :=
    is_localization.is_domain_localization (le_non_zero_divisors_of_no_zero_divisors hM'),
  suffices : (⊥ : ideal (localization M')).is_maximal,
  { rw le_antisymm bot_le (comap_bot_le_of_injective _ (is_localization.map_injective_of_injective
      M (localization M) (localization M')
      quotient_map_injective (le_non_zero_divisors_of_no_zero_divisors hM'))),
    refine is_maximal_comap_of_is_integral_of_is_maximal' _ _ ⊥ this,
    apply is_integral_is_localization_polynomial_quotient P _ (submodule.coe_mem m) },
  rw (map_bot.symm : (⊥ : ideal (localization M')) =
                     map (algebra_map (R[X] ⧸ P) (localization M')) ⊥),
  let bot_maximal := ((bot_quotient_is_maximal_iff _).mpr hP),
  refine map.is_maximal (algebra_map _ _) (is_field.localization_map_bijective hM' _) bot_maximal,
  rwa [← quotient.maximal_ideal_iff_is_field_quotient, ← bot_quotient_is_maximal_iff],
end

/-- Used to bootstrap the more general `quotient_mk_comp_C_is_integral_of_jacobson` -/
private lemma quotient_mk_comp_C_is_integral_of_jacobson' [nontrivial R] (hR : is_jacobson R)
  (hP' : ∀ (x : R), C x ∈ P → x = 0) :
  ((quotient.mk P).comp C : R →+* R[X] ⧸ P).is_integral :=
begin
  refine (is_integral_quotient_map_iff _).mp _,
  let P' : ideal R := P.comap C,
  obtain ⟨pX, hpX, hp0⟩ :=
    exists_nonzero_mem_of_ne_bot (ne_of_lt (bot_lt_of_maximal P polynomial_not_is_field)).symm hP',
  let M : submonoid (R ⧸ P') := submonoid.powers (pX.map (quotient.mk P')).leading_coeff,
  let φ : R ⧸ P' →+* R[X] ⧸ P := quotient_map P C le_rfl,
  haveI hp'_prime : P'.is_prime := comap_is_prime C P,
  have hM : (0 : R ⧸ P') ∉ M := λ ⟨n, hn⟩, hp0 $ leading_coeff_eq_zero.mp (pow_eq_zero hn),
  let M' : submonoid (R[X] ⧸ P) := M.map (quotient_map P C le_rfl),
  refine ((quotient_map P C le_rfl).is_integral_tower_bot_of_is_integral
    (algebra_map _ (localization M')) _ _),
  { refine is_localization.injective (localization M')
      (show M' ≤ _, from le_non_zero_divisors_of_no_zero_divisors (λ hM', hM _)),
    exact (let ⟨z, zM, z0⟩ := hM' in (quotient_map_injective (trans z0 φ.map_zero.symm)) ▸ zM) },
  { rw ← is_localization.map_comp M.le_comap_map,
    refine ring_hom.is_integral_trans (algebra_map (R ⧸ P') (localization M))
      (is_localization.map _ _ M.le_comap_map) _ _,
    { exact (algebra_map (R ⧸ P') (localization M)).is_integral_of_surjective
      (is_field.localization_map_bijective hM ((quotient.maximal_ideal_iff_is_field_quotient _).mp
                                               (is_maximal_comap_C_of_is_maximal P hP'))).2 },
    { -- `convert` here is faster than `exact`, and this proof is near the time limit.
      convert is_integral_is_localization_polynomial_quotient P pX hpX } }
end

/-- If `R` is a Jacobson ring, and `P` is a maximal ideal of `polynomial R`,
  then `R → R[X]/P` is an integral map. -/
lemma quotient_mk_comp_C_is_integral_of_jacobson :
  ((quotient.mk P).comp C : R →+* R[X] ⧸ P).is_integral :=
begin
  let P' : ideal R := P.comap C,
  haveI : P'.is_prime := comap_is_prime C P,
  let f : R[X] →+* polynomial (R ⧸ P') := polynomial.map_ring_hom (quotient.mk P'),
  have hf : function.surjective f := map_surjective (quotient.mk P') quotient.mk_surjective,
  have hPJ : P = (P.map f).comap f,
  { rw comap_map_of_surjective _ hf,
    refine le_antisymm (le_sup_of_le_left le_rfl) (sup_le le_rfl _),
    refine λ p hp, polynomial_mem_ideal_of_coeff_mem_ideal P p (λ n, quotient.eq_zero_iff_mem.mp _),
    simpa only [coeff_map, coe_map_ring_hom] using (polynomial.ext_iff.mp hp) n },
  refine ring_hom.is_integral_tower_bot_of_is_integral _ _ (injective_quotient_le_comap_map P) _,
  rw ← quotient_mk_maps_eq,
  refine ring_hom.is_integral_trans _ _
    ((quotient.mk P').is_integral_of_surjective quotient.mk_surjective) _,
  apply quotient_mk_comp_C_is_integral_of_jacobson' _ _ (λ x hx, _),
  any_goals { exact ideal.is_jacobson_quotient },
  { exact or.rec_on (map_eq_top_or_is_maximal_of_surjective f hf hP)
    (λ h, absurd (trans (h ▸ hPJ : P = comap f ⊤) comap_top : P = ⊤) hP.ne_top) id },
  { apply_instance, },
  { obtain ⟨z, rfl⟩ := quotient.mk_surjective x,
    rwa [quotient.eq_zero_iff_mem, mem_comap, hPJ, mem_comap, coe_map_ring_hom, map_C] }
end

lemma is_maximal_comap_C_of_is_jacobson :
  (P.comap (C : R →+* R[X])).is_maximal :=
begin
  rw [← @mk_ker _ _ P, ring_hom.ker_eq_comap_bot, comap_comap],
  exact is_maximal_comap_of_is_integral_of_is_maximal' _
    (quotient_mk_comp_C_is_integral_of_jacobson P) ⊥ ((bot_quotient_is_maximal_iff _).mpr hP),
end

omit P hP

lemma comp_C_integral_of_surjective_of_jacobson
  {S : Type*} [field S] (f : R[X] →+* S) (hf : function.surjective f) :
  (f.comp C).is_integral :=
begin
  haveI : (f.ker).is_maximal := f.ker_is_maximal_of_surjective hf,
  let g : R[X] ⧸ f.ker →+* S := ideal.quotient.lift f.ker f (λ _ h, h),
  have hfg : (g.comp (quotient.mk f.ker)) = f := ring_hom_ext' rfl rfl,
  rw [← hfg, ring_hom.comp_assoc],
  refine ring_hom.is_integral_trans _ g (quotient_mk_comp_C_is_integral_of_jacobson f.ker)
    (g.is_integral_of_surjective _), --(quotient.lift_surjective f.ker f _ hf)),
  rw [← hfg] at hf,
  exact function.surjective.of_comp hf,
end

end

end polynomial

open mv_polynomial ring_hom

namespace mv_polynomial

lemma is_jacobson_mv_polynomial_fin {R : Type*} [comm_ring R] [H : is_jacobson R] :
  ∀ (n : ℕ), is_jacobson (mv_polynomial (fin n) R)
| 0 := ((is_jacobson_iso ((rename_equiv R
  (equiv.equiv_pempty (fin 0))).to_ring_equiv.trans (is_empty_ring_equiv R pempty))).mpr H)
| (n+1) := (is_jacobson_iso (fin_succ_equiv R n).to_ring_equiv).2
  (polynomial.is_jacobson_polynomial_iff_is_jacobson.2 (is_jacobson_mv_polynomial_fin n))

/-- General form of the nullstellensatz for Jacobson rings, since in a Jacobson ring we have
  `Inf {P maximal | P ≥ I} = Inf {P prime | P ≥ I} = I.radical`. Fields are always Jacobson,
  and in that special case this is (most of) the classical Nullstellensatz,
  since `I(V(I))` is the intersection of maximal ideals containing `I`, which is then `I.radical` -/
instance {R : Type*} [comm_ring R] {ι : Type*} [fintype ι] [is_jacobson R] :
  is_jacobson (mv_polynomial ι R) :=
begin
  haveI := classical.dec_eq ι,
  let e := fintype.equiv_fin ι,
  rw is_jacobson_iso (rename_equiv R e).to_ring_equiv,
  exact is_jacobson_mv_polynomial_fin _
end

variables {n : ℕ}

lemma quotient_mk_comp_C_is_integral_of_jacobson
  {R : Type*} [comm_ring R] [is_jacobson R]
  (P : ideal (mv_polynomial (fin n) R)) [P.is_maximal] :
  ((quotient.mk P).comp mv_polynomial.C : R →+* mv_polynomial _ R ⧸ P).is_integral :=
begin
  unfreezingI {induction n with n IH},
  { refine ring_hom.is_integral_of_surjective _ (function.surjective.comp quotient.mk_surjective _),
    exact C_surjective (fin 0) },
  { rw [← fin_succ_equiv_comp_C_eq_C, ← ring_hom.comp_assoc, ← ring_hom.comp_assoc,
      ← quotient_map_comp_mk le_rfl, ring_hom.comp_assoc (polynomial.C),
      ← quotient_map_comp_mk le_rfl, ring_hom.comp_assoc, ring_hom.comp_assoc,
      ← quotient_map_comp_mk le_rfl, ← ring_hom.comp_assoc (quotient.mk _)],
    refine ring_hom.is_integral_trans _ _ _ _,
    { refine ring_hom.is_integral_trans _ _ (is_integral_of_surjective _ quotient.mk_surjective) _,
      refine ring_hom.is_integral_trans _ _ _ _,
      { apply (is_integral_quotient_map_iff _).mpr (IH _),
        apply polynomial.is_maximal_comap_C_of_is_jacobson _,
        { exact mv_polynomial.is_jacobson_mv_polynomial_fin n },
        { apply comap_is_maximal_of_surjective,
          exact (fin_succ_equiv R n).symm.surjective } },
      { refine (is_integral_quotient_map_iff _).mpr _,
        rw ← quotient_map_comp_mk le_rfl,
        refine ring_hom.is_integral_trans _ _ _ ((is_integral_quotient_map_iff _).mpr _),
        { exact ring_hom.is_integral_of_surjective _ quotient.mk_surjective },
        { apply polynomial.quotient_mk_comp_C_is_integral_of_jacobson _,
          { exact mv_polynomial.is_jacobson_mv_polynomial_fin n },
          { exact comap_is_maximal_of_surjective _ (fin_succ_equiv R n).symm.surjective } } } },
    { refine (is_integral_quotient_map_iff _).mpr _,
      refine ring_hom.is_integral_trans _ _ _ (is_integral_of_surjective _ quotient.mk_surjective),
      exact ring_hom.is_integral_of_surjective _ (fin_succ_equiv R n).symm.surjective } }
end

lemma comp_C_integral_of_surjective_of_jacobson
  {R : Type*} [comm_ring R] [is_jacobson R]
  {σ : Type*} [fintype σ] {S : Type*} [field S] (f : mv_polynomial σ R →+* S)
  (hf : function.surjective f) : (f.comp C).is_integral :=
begin
  haveI := classical.dec_eq σ,
  obtain ⟨e⟩ := fintype.trunc_equiv_fin σ,
  let f' : mv_polynomial (fin _) R →+* S :=
    f.comp (rename_equiv R e.symm).to_ring_equiv.to_ring_hom,
  have hf' : function.surjective f' :=
    ((function.surjective.comp hf (rename_equiv R e.symm).surjective)),
  have : (f'.comp C).is_integral,
  { haveI : (f'.ker).is_maximal := f'.ker_is_maximal_of_surjective hf',
    let g : mv_polynomial _ R ⧸ f'.ker →+* S := ideal.quotient.lift f'.ker f' (λ _ h, h),
    have hfg : (g.comp (quotient.mk f'.ker)) = f' := ring_hom_ext (λ r, rfl) (λ i, rfl),
    rw [← hfg, ring_hom.comp_assoc],
    refine ring_hom.is_integral_trans _ g (quotient_mk_comp_C_is_integral_of_jacobson f'.ker)
      (g.is_integral_of_surjective _),
    rw ← hfg at hf',
    exact function.surjective.of_comp hf' },
  rw ring_hom.comp_assoc at this,
  convert this,
  refine ring_hom.ext (λ x, _),
  exact ((rename_equiv R e.symm).commutes' x).symm,
end

end mv_polynomial

end ideal