/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Yury Kudryashov, Yaël Dillies

! This file was ported from Lean 3 source module algebra.module.big_operators
! leanprover-community/mathlib commit 327c3c0d9232d80e250dc8f65e7835b82b266ea5
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Module.Basic
import Mathbin.GroupTheory.GroupAction.BigOperators

/-!
# Finite sums over modules over a ring

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

open BigOperators

variable {α β R M ι : Type _}

section AddCommMonoid

variable [Semiring R] [AddCommMonoid M] [Module R M] (r s : R) (x y : M)

/- warning: list.sum_smul -> List.sum_smul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2] {l : List.{u1} R} {x : M}, Eq.{succ u2} M (SMul.smul.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R _inst_1) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Module.toMulActionWithZero.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (List.sum.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) l) x) (List.sum.{u2} M (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (List.map.{u1, u2} R M (fun (r : R) => SMul.smul.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R _inst_1) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Module.toMulActionWithZero.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) r x) l))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} [_inst_1 : Semiring.{u2} R] [_inst_2 : AddCommMonoid.{u1} M] [_inst_3 : Module.{u2, u1} R M _inst_1 _inst_2] {l : List.{u2} R} {x : M}, Eq.{succ u1} M (HSMul.hSMul.{u2, u1, u1} R M M (instHSMul.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R _inst_1)) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (MulActionWithZero.toSMulWithZero.{u2, u1} R M (Semiring.toMonoidWithZero.{u2} R _inst_1) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (Module.toMulActionWithZero.{u2, u1} R M _inst_1 _inst_2 _inst_3))))) (List.sum.{u2} R (Distrib.toAdd.{u2} R (NonUnitalNonAssocSemiring.toDistrib.{u2} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R (Semiring.toNonAssocSemiring.{u2} R _inst_1)))) (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R _inst_1)) l) x) (List.sum.{u1} M (AddZeroClass.toAdd.{u1} M (AddMonoid.toAddZeroClass.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2))) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (List.map.{u2, u1} R M (fun (r : R) => HSMul.hSMul.{u2, u1, u1} R M M (instHSMul.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R _inst_1)) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (MulActionWithZero.toSMulWithZero.{u2, u1} R M (Semiring.toMonoidWithZero.{u2} R _inst_1) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (Module.toMulActionWithZero.{u2, u1} R M _inst_1 _inst_2 _inst_3))))) r x) l))
Case conversion may be inaccurate. Consider using '#align list.sum_smul List.sum_smulₓ'. -/
theorem List.sum_smul {l : List R} {x : M} : l.Sum • x = (l.map fun r => r • x).Sum :=
  ((smulAddHom R M).flip x).map_list_sum l
#align list.sum_smul List.sum_smul

/- warning: multiset.sum_smul -> Multiset.sum_smul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2] {l : Multiset.{u1} R} {x : M}, Eq.{succ u2} M (SMul.smul.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R _inst_1) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Module.toMulActionWithZero.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (Multiset.sum.{u1} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) l) x) (Multiset.sum.{u2} M _inst_2 (Multiset.map.{u1, u2} R M (fun (r : R) => SMul.smul.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R _inst_1) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Module.toMulActionWithZero.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) r x) l))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} [_inst_1 : Semiring.{u2} R] [_inst_2 : AddCommMonoid.{u1} M] [_inst_3 : Module.{u2, u1} R M _inst_1 _inst_2] {l : Multiset.{u2} R} {x : M}, Eq.{succ u1} M (HSMul.hSMul.{u2, u1, u1} R M M (instHSMul.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R _inst_1)) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (MulActionWithZero.toSMulWithZero.{u2, u1} R M (Semiring.toMonoidWithZero.{u2} R _inst_1) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (Module.toMulActionWithZero.{u2, u1} R M _inst_1 _inst_2 _inst_3))))) (Multiset.sum.{u2} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R (Semiring.toNonAssocSemiring.{u2} R _inst_1))) l) x) (Multiset.sum.{u1} M _inst_2 (Multiset.map.{u2, u1} R M (fun (r : R) => HSMul.hSMul.{u2, u1, u1} R M M (instHSMul.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R _inst_1)) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (MulActionWithZero.toSMulWithZero.{u2, u1} R M (Semiring.toMonoidWithZero.{u2} R _inst_1) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (Module.toMulActionWithZero.{u2, u1} R M _inst_1 _inst_2 _inst_3))))) r x) l))
Case conversion may be inaccurate. Consider using '#align multiset.sum_smul Multiset.sum_smulₓ'. -/
theorem Multiset.sum_smul {l : Multiset R} {x : M} : l.Sum • x = (l.map fun r => r • x).Sum :=
  ((smulAddHom R M).flip x).map_multiset_sum l
#align multiset.sum_smul Multiset.sum_smul

/- warning: multiset.sum_smul_sum -> Multiset.sum_smul_sum is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2] {s : Multiset.{u1} R} {t : Multiset.{u2} M}, Eq.{succ u2} M (SMul.smul.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R _inst_1) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Module.toMulActionWithZero.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (Multiset.sum.{u1} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) s) (Multiset.sum.{u2} M _inst_2 t)) (Multiset.sum.{u2} M _inst_2 (Multiset.map.{max u1 u2, u2} (Prod.{u1, u2} R M) M (fun (p : Prod.{u1, u2} R M) => SMul.smul.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R _inst_1) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Module.toMulActionWithZero.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (Prod.fst.{u1, u2} R M p) (Prod.snd.{u1, u2} R M p)) (Multiset.product.{u1, u2} R M s t)))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} [_inst_1 : Semiring.{u2} R] [_inst_2 : AddCommMonoid.{u1} M] [_inst_3 : Module.{u2, u1} R M _inst_1 _inst_2] {s : Multiset.{u2} R} {t : Multiset.{u1} M}, Eq.{succ u1} M (HSMul.hSMul.{u2, u1, u1} R M M (instHSMul.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R _inst_1)) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (MulActionWithZero.toSMulWithZero.{u2, u1} R M (Semiring.toMonoidWithZero.{u2} R _inst_1) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (Module.toMulActionWithZero.{u2, u1} R M _inst_1 _inst_2 _inst_3))))) (Multiset.sum.{u2} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u2} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R (Semiring.toNonAssocSemiring.{u2} R _inst_1))) s) (Multiset.sum.{u1} M _inst_2 t)) (Multiset.sum.{u1} M _inst_2 (Multiset.map.{max u2 u1, u1} (Prod.{u2, u1} R M) M (fun (p : Prod.{u2, u1} R M) => HSMul.hSMul.{u2, u1, u1} R M M (instHSMul.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R _inst_1)) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (MulActionWithZero.toSMulWithZero.{u2, u1} R M (Semiring.toMonoidWithZero.{u2} R _inst_1) (AddMonoid.toZero.{u1} M (AddCommMonoid.toAddMonoid.{u1} M _inst_2)) (Module.toMulActionWithZero.{u2, u1} R M _inst_1 _inst_2 _inst_3))))) (Prod.fst.{u2, u1} R M p) (Prod.snd.{u2, u1} R M p)) (Multiset.product.{u2, u1} R M s t)))
Case conversion may be inaccurate. Consider using '#align multiset.sum_smul_sum Multiset.sum_smul_sumₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem Multiset.sum_smul_sum {s : Multiset R} {t : Multiset M} :
    s.Sum • t.Sum = ((s ×ˢ t).map fun p : R × M => p.fst • p.snd).Sum :=
  by
  induction' s using Multiset.induction with a s ih
  · simp
  · simp [add_smul, ih, ← Multiset.smul_sum]
#align multiset.sum_smul_sum Multiset.sum_smul_sum

#print Finset.sum_smul /-
theorem Finset.sum_smul {f : ι → R} {s : Finset ι} {x : M} :
    (∑ i in s, f i) • x = ∑ i in s, f i • x :=
  ((smulAddHom R M).flip x).map_sum f s
#align finset.sum_smul Finset.sum_smul
-/

/- warning: finset.sum_smul_sum -> Finset.sum_smul_sum is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {R : Type.{u3}} {M : Type.{u4}} [_inst_1 : Semiring.{u3} R] [_inst_2 : AddCommMonoid.{u4} M] [_inst_3 : Module.{u3, u4} R M _inst_1 _inst_2] {f : α -> R} {g : β -> M} {s : Finset.{u1} α} {t : Finset.{u2} β}, Eq.{succ u4} M (SMul.smul.{u3, u4} R M (SMulZeroClass.toHasSmul.{u3, u4} R M (AddZeroClass.toHasZero.{u4} M (AddMonoid.toAddZeroClass.{u4} M (AddCommMonoid.toAddMonoid.{u4} M _inst_2))) (SMulWithZero.toSmulZeroClass.{u3, u4} R M (MulZeroClass.toHasZero.{u3} R (MulZeroOneClass.toMulZeroClass.{u3} R (MonoidWithZero.toMulZeroOneClass.{u3} R (Semiring.toMonoidWithZero.{u3} R _inst_1)))) (AddZeroClass.toHasZero.{u4} M (AddMonoid.toAddZeroClass.{u4} M (AddCommMonoid.toAddMonoid.{u4} M _inst_2))) (MulActionWithZero.toSMulWithZero.{u3, u4} R M (Semiring.toMonoidWithZero.{u3} R _inst_1) (AddZeroClass.toHasZero.{u4} M (AddMonoid.toAddZeroClass.{u4} M (AddCommMonoid.toAddMonoid.{u4} M _inst_2))) (Module.toMulActionWithZero.{u3, u4} R M _inst_1 _inst_2 _inst_3)))) (Finset.sum.{u3, u1} R α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} R (Semiring.toNonAssocSemiring.{u3} R _inst_1))) s (fun (i : α) => f i)) (Finset.sum.{u4, u2} M β _inst_2 t (fun (i : β) => g i))) (Finset.sum.{u4, max u1 u2} M (Prod.{u1, u2} α β) _inst_2 (Finset.product.{u1, u2} α β s t) (fun (p : Prod.{u1, u2} α β) => SMul.smul.{u3, u4} R M (SMulZeroClass.toHasSmul.{u3, u4} R M (AddZeroClass.toHasZero.{u4} M (AddMonoid.toAddZeroClass.{u4} M (AddCommMonoid.toAddMonoid.{u4} M _inst_2))) (SMulWithZero.toSmulZeroClass.{u3, u4} R M (MulZeroClass.toHasZero.{u3} R (MulZeroOneClass.toMulZeroClass.{u3} R (MonoidWithZero.toMulZeroOneClass.{u3} R (Semiring.toMonoidWithZero.{u3} R _inst_1)))) (AddZeroClass.toHasZero.{u4} M (AddMonoid.toAddZeroClass.{u4} M (AddCommMonoid.toAddMonoid.{u4} M _inst_2))) (MulActionWithZero.toSMulWithZero.{u3, u4} R M (Semiring.toMonoidWithZero.{u3} R _inst_1) (AddZeroClass.toHasZero.{u4} M (AddMonoid.toAddZeroClass.{u4} M (AddCommMonoid.toAddMonoid.{u4} M _inst_2))) (Module.toMulActionWithZero.{u3, u4} R M _inst_1 _inst_2 _inst_3)))) (f (Prod.fst.{u1, u2} α β p)) (g (Prod.snd.{u1, u2} α β p))))
but is expected to have type
  forall {α : Type.{u4}} {β : Type.{u3}} {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2] {f : α -> R} {g : β -> M} {s : Finset.{u4} α} {t : Finset.{u3} β}, Eq.{succ u2} M (HSMul.hSMul.{u1, u2, u2} R M M (instHSMul.{u1, u2} R M (SMulZeroClass.toSMul.{u1, u2} R M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (SMulWithZero.toSMulZeroClass.{u1, u2} R M (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)) (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R _inst_1) (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (Module.toMulActionWithZero.{u1, u2} R M _inst_1 _inst_2 _inst_3))))) (Finset.sum.{u1, u4} R α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) s (fun (i : α) => f i)) (Finset.sum.{u2, u3} M β _inst_2 t (fun (i : β) => g i))) (Finset.sum.{u2, max u3 u4} M (Prod.{u4, u3} α β) _inst_2 (Finset.product.{u4, u3} α β s t) (fun (p : Prod.{u4, u3} α β) => HSMul.hSMul.{u1, u2, u2} R M M (instHSMul.{u1, u2} R M (SMulZeroClass.toSMul.{u1, u2} R M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (SMulWithZero.toSMulZeroClass.{u1, u2} R M (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)) (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R _inst_1) (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (Module.toMulActionWithZero.{u1, u2} R M _inst_1 _inst_2 _inst_3))))) (f (Prod.fst.{u4, u3} α β p)) (g (Prod.snd.{u4, u3} α β p))))
Case conversion may be inaccurate. Consider using '#align finset.sum_smul_sum Finset.sum_smul_sumₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem Finset.sum_smul_sum {f : α → R} {g : β → M} {s : Finset α} {t : Finset β} :
    ((∑ i in s, f i) • ∑ i in t, g i) = ∑ p in s ×ˢ t, f p.fst • g p.snd :=
  by
  rw [Finset.sum_product, Finset.sum_smul, Finset.sum_congr rfl]
  intros
  rw [Finset.smul_sum]
#align finset.sum_smul_sum Finset.sum_smul_sum

end AddCommMonoid

#print Finset.cast_card /-
theorem Finset.cast_card [CommSemiring R] (s : Finset α) : (s.card : R) = ∑ a in s, 1 := by
  rw [Finset.sum_const, Nat.smul_one_eq_coe]
#align finset.cast_card Finset.cast_card
-/


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.algebra.basic
import Mathlib.algebra.algebra.subalgebra
import Mathlib.algebra.free_algebra
import Mathlib.algebra.category.CommRing.basic
import Mathlib.algebra.category.Module.basic
import Mathlib.PostPort

universes v u l u_1

namespace Mathlib

/-- The category of R-modules and their morphisms. -/
structure Algebra (R : Type u) [comm_ring R]
where
  carrier : Type v
  is_ring : ring carrier
  is_algebra : algebra R carrier

namespace Algebra

protected instance has_coe_to_sort (R : Type u) [comm_ring R] : has_coe_to_sort (Algebra R) :=
  has_coe_to_sort.mk (Type v) carrier

protected instance category_theory.category (R : Type u) [comm_ring R] : category_theory.category (Algebra R) :=
  category_theory.category.mk

protected instance category_theory.concrete_category (R : Type u) [comm_ring R] : category_theory.concrete_category (Algebra R) :=
  category_theory.concrete_category.mk
    (category_theory.functor.mk (fun (R_1 : Algebra R) => ↥R_1) fun (R_1 S : Algebra R) (f : R_1 ⟶ S) => ⇑f)

protected instance has_forget_to_Ring (R : Type u) [comm_ring R] : category_theory.has_forget₂ (Algebra R) Ring :=
  category_theory.has_forget₂.mk
    (category_theory.functor.mk (fun (A : Algebra R) => Ring.of ↥A)
      fun (A₁ A₂ : Algebra R) (f : A₁ ⟶ A₂) => alg_hom.to_ring_hom f)

protected instance has_forget_to_Module (R : Type u) [comm_ring R] : category_theory.has_forget₂ (Algebra R) (Module R) :=
  category_theory.has_forget₂.mk
    (category_theory.functor.mk (fun (M : Algebra R) => Module.of R ↥M)
      fun (M₁ M₂ : Algebra R) (f : M₁ ⟶ M₂) => alg_hom.to_linear_map f)

/-- The object in the category of R-algebras associated to a type equipped with the appropriate
typeclasses. -/
def of (R : Type u) [comm_ring R] (X : Type v) [ring X] [algebra R X] : Algebra R :=
  mk X

protected instance inhabited (R : Type u) [comm_ring R] : Inhabited (Algebra R) :=
  { default := of R R }

@[simp] theorem coe_of (R : Type u) [comm_ring R] (X : Type u) [ring X] [algebra R X] : ↥(of R X) = X :=
  rfl

/-- Forgetting to the underlying type and then building the bundled object returns the original
algebra. -/
@[simp] theorem of_self_iso_hom {R : Type u} [comm_ring R] (M : Algebra R) : category_theory.iso.hom (of_self_iso M) = 𝟙 :=
  Eq.refl (category_theory.iso.hom (of_self_iso M))

@[simp] theorem id_apply {R : Type u} [comm_ring R] {M : Module R} (m : ↥M) : coe_fn 𝟙 m = m :=
  rfl

@[simp] theorem coe_comp {R : Type u} [comm_ring R] {M : Module R} {N : Module R} {U : Module R} (f : M ⟶ N) (g : N ⟶ U) : ⇑(f ≫ g) = ⇑g ∘ ⇑f :=
  rfl

/-- The "free algebra" functor, sending a type `S` to the free algebra on `S`. -/
@[simp] theorem free_obj_is_algebra (R : Type u) [comm_ring R] (S : Type u_1) : is_algebra (category_theory.functor.obj (free R) S) = free_algebra.algebra R S :=
  Eq.refl (is_algebra (category_theory.functor.obj (free R) S))

/-- The free/forget ajunction for `R`-algebras. -/
def adj (R : Type u) [comm_ring R] : free R ⊣ category_theory.forget (Algebra R) :=
  category_theory.adjunction.mk_of_hom_equiv
    (category_theory.adjunction.core_hom_equiv.mk
      fun (X : Type (max u u_1)) (A : Algebra R) => equiv.symm (free_algebra.lift R))

end Algebra

/-- Build an isomorphism in the category `Algebra R` from a `alg_equiv` between `algebra`s. -/
@[simp] theorem alg_equiv.to_Algebra_iso_hom {R : Type u} [comm_ring R] {X₁ : Type u} {X₂ : Type u} {g₁ : ring X₁} {g₂ : ring X₂} {m₁ : algebra R X₁} {m₂ : algebra R X₂} (e : alg_equiv R X₁ X₂) : category_theory.iso.hom (alg_equiv.to_Algebra_iso e) = ↑e :=
  Eq.refl (category_theory.iso.hom (alg_equiv.to_Algebra_iso e))

namespace category_theory.iso

/-- Build a `alg_equiv` from an isomorphism in the category `Algebra R`. -/
@[simp] theorem to_alg_equiv_apply {R : Type u} [comm_ring R] {X : Algebra R} {Y : Algebra R} (i : X ≅ Y) : ∀ (ᾰ : ↥X), coe_fn (to_alg_equiv i) ᾰ = coe_fn (hom i) ᾰ :=
  fun (ᾰ : ↥X) => Eq.refl (coe_fn (to_alg_equiv i) ᾰ)

end category_theory.iso

/-- Algebra equivalences between `algebras`s are the same as (isomorphic to) isomorphisms in
`Algebra`. -/
def alg_equiv_iso_Algebra_iso {R : Type u} [comm_ring R] {X : Type u} {Y : Type u} [ring X] [ring Y] [algebra R X] [algebra R Y] : alg_equiv R X Y ≅ Algebra.of R X ≅ Algebra.of R Y :=
  category_theory.iso.mk (fun (e : alg_equiv R X Y) => alg_equiv.to_Algebra_iso e)
    fun (i : Algebra.of R X ≅ Algebra.of R Y) => category_theory.iso.to_alg_equiv i

protected instance Algebra.has_coe {R : Type u} [comm_ring R] (X : Type u) [ring X] [algebra R X] : has_coe (subalgebra R X) (Algebra R) :=
  has_coe.mk fun (N : subalgebra R X) => Algebra.of R ↥N

protected instance Algebra.forget_reflects_isos {R : Type u} [comm_ring R] : category_theory.reflects_isomorphisms (category_theory.forget (Algebra R)) :=
  category_theory.reflects_isomorphisms.mk
    fun (X Y : Algebra R) (f : X ⟶ Y)
      (_x : category_theory.is_iso (category_theory.functor.map (category_theory.forget (Algebra R)) f)) =>
      let i :
        category_theory.functor.obj (category_theory.forget (Algebra R)) X ≅
          category_theory.functor.obj (category_theory.forget (Algebra R)) Y :=
        category_theory.as_iso (category_theory.functor.map (category_theory.forget (Algebra R)) f);
      let e : alg_equiv R ↥X ↥Y :=
        alg_equiv.mk (alg_hom.to_fun f) (equiv.inv_fun (category_theory.iso.to_equiv i)) sorry sorry sorry sorry sorry;
      category_theory.is_iso.mk (category_theory.iso.inv (alg_equiv.to_Algebra_iso e))


import Logic.Predicate.Term

universe u u₁ u₂ v v₁ v₂ w w₁ w₂

variable
  {L : Language.{u}} {L₁ : Language.{u₁}} {L₂ : Language.{u₂}}
  {μ : Type v} {μ₁ : Type v₁} {μ₂ : Type v₂}

namespace FirstOrder

@[ext] class Structure (L : Language.{u}) (M : Type w) where
  func : {k : ℕ} → L.func k → (Fin k → M) → M
  rel  : {k : ℕ} → L.rel k → (Fin k → M) → Prop

end FirstOrder

namespace Language

namespace Hom

open FirstOrder

def onStructure (Φ : L₁ →ᵥ L₂) {M : Type w} (S : Structure L₂ M) : Structure L₁ M where
  func := fun f => S.func (Φ.onFunc f)
  rel := fun r => S.rel (Φ.onRel r)

instance subLanguageStructure {pf : ∀ k, Language.func L k → Prop} {pr : ∀ k, L.rel k → Prop}
  {M : Type w} (s : Structure L M) : Structure (subLanguage L pf pr) M :=
  onStructure (ofSubLanguage L) s

noncomputable def extendStructure (Φ : L₁ →ᵥ L₂) {M : Type w} [Inhabited M] (s : Structure L₁ M) : Structure L₂ M where
  func := fun {k} f₂ v => Classical.epsilon (fun y => ∃ f₁ : L₁.func k, Φ.onFunc f₁ = f₂ ∧ y = s.func f₁ v)
  rel  := fun {k} r₂ v => ∃ r₁ : L₁.rel k, Φ.onRel r₁ = r₂ ∧ s.rel r₁ v

end Hom
end Language

namespace FirstOrder

namespace Structure

instance [Inhabited M] : Inhabited (Structure L M) :=
⟨{ func := fun _ _ => default, rel := fun _ _ => True }⟩

variable (Φ : L₁ →ᵥ L₂) {M : Type w} (s₂ : Structure L₂ M)

@[simp] lemma onStructure_func {k} {f : L₁.func k} {v : Fin k → M} :
    (Φ.onStructure s₂).func f v = s₂.func (Φ.onFunc f) v := rfl

@[simp] lemma onStructure_rel {k} {r : L₁.rel k} {v : Fin k → M} :
    (Φ.onStructure s₂).rel r v ↔ s₂.rel (Φ.onRel r) v := of_eq rfl

variable [Inhabited M] (s₁ : Structure L₁ M)

lemma extendStructure_func
  {k} (injf : Function.Injective (Φ.onFunc : L₁.func k → L₂.func k)) (f₁ : L₁.func k) (v : Fin k → M) :
    (Φ.extendStructure s₁).func (Φ.onFunc f₁) v = s₁.func f₁ v := by
  simp[Language.Hom.extendStructure]
  have : ∃ y, ∃ f₁' : L₁.func k, Φ.onFunc f₁' = Φ.onFunc f₁ ∧ y = s₁.func f₁' v := ⟨s₁.func f₁ v, f₁, rfl, rfl⟩
  rcases Classical.epsilon_spec this with ⟨f', f'eq, h⟩
  rcases injf f'eq with rfl; exact h

lemma extendStructure_rel
  {k} (injr : Function.Injective (Φ.onRel : L₁.rel k → L₂.rel k)) (r₁ : L₁.rel k) (v : Fin k → M) :
    (Φ.extendStructure s₁).rel (Φ.onRel r₁) v ↔ s₁.rel r₁ v := by
  simp[Language.Hom.extendStructure]
  refine ⟨by intros h; rcases h with ⟨r₁', e, h⟩; rcases injr e; exact h, by intros h; refine ⟨r₁, rfl, h⟩⟩

class Eq (L : Language.{u}) [L.HasEq] (M : Type w) [s : Structure L M] where
  eq : ∀ a b, s.rel Language.HasEq.eq ![a, b] ↔ a = b

attribute [simp] Eq.eq

end Structure

end FirstOrder

namespace SubTerm

open FirstOrder

variable {M} (s : Structure L M) {n n₁ n₂ : ℕ} (e : Fin n → M) (e₂ : Fin n₂ → M) (ε : μ → M) (ε₂ : μ₂ → M)

def val : SubTerm L μ n → M
  | #x       => e x
  | &x       => ε x
  | func f v => s.func f (fun i => (v i).val)

variable (M) {s}

@[reducible] def val! (M : Type w) [s : Structure L M] {n} (e : Fin n → M) (ε : μ → M) : SubTerm L μ n → M := val s e ε

variable {M e e₂ ε ε₂}

@[simp] lemma val_bvar (x) : val s e ε (#x : SubTerm L μ n) = e x := rfl

@[simp] lemma val_fvar (x) : val s e ε (&x : SubTerm L μ n) = ε x := rfl

lemma val_func {k} (f : L.func k) (v) :
    val s e ε (func f v) = s.func f (fun i => (v i).val s e ε) := rfl

lemma val_bind (bound : Fin n₁ → SubTerm L μ₂ n₂) (free : μ₁ → SubTerm L μ₂ n₂) (t : SubTerm L μ₁ n₁) :
    (bind bound free t).val s e₂ ε₂ = t.val s (val s e₂ ε₂ ∘ bound) (val s e₂ ε₂ ∘ free) :=
  by induction t <;> simp[*, bind_func, val_func]

lemma val_map (bound : Fin n₁ → Fin n₂) (free : μ₁ → μ₂) (t : SubTerm L μ₁ n₁) :
    (map bound free t).val s e₂ ε₂ = t.val s (e₂ ∘ bound) (ε₂ ∘ free) := val_bind _ _ _

lemma val_subst (u : SubTerm L μ n) (t : SubTerm L μ (n + 1)) :
    (subst u t).val s e ε = t.val s (e <: u.val s e ε) ε :=
  by simp[subst, val_bind]; congr; exact funext $ Fin.lastCases (by simp) (by simp)

@[simp] lemma val_bShift (a : M) (t : SubTerm L μ n) :
    t.bShift.val s (a :> e) ε = t.val s e ε := by simp[bShift, val_map, Function.comp]

section Language

variable (Φ : L₁ →ᵥ L₂) (e : Fin n → M) (ε : μ → M)

lemma val_onSubTerm (s₂ : Structure L₂ M) {t : SubTerm L₁ μ n} :
    val s₂ e ε (Φ.onSubTerm t) = val (Φ.onStructure s₂) e ε t :=
  by induction t <;> simp[*, val!, Function.comp, val_func, Language.Hom.onSubTerm_func]

variable [Inhabited M]

lemma val_extendStructure_onSubTerm
    (injf : ∀ k, Function.Injective (Φ.onFunc : L₁.func k → L₂.func k))
    (s₁ : Structure L₁ M) (t : SubTerm L₁ μ n) :
    val (Φ.extendStructure s₁) e ε (Φ.onSubTerm t) = val s₁ e ε t := by
  induction t <;> simp[*, Language.Hom.onSubTerm_func, val_func]
  case func k f v ih =>
    exact Structure.extendStructure_func Φ s₁ (injf k) f (fun i => val s₁ e ε (v i))

end Language

section Syntactic

variable (ε : ℕ → M)

lemma val_shift (t : SyntacticSubTerm L n) :
    t.shift.val s e ε = t.val s e (ε ∘ Nat.succ) := by simp[shift, val_map]

lemma val_free (a : M) (t : SyntacticSubTerm L (n + 1)) :
    t.free.val s e (a :>ₙ ε) = t.val s (e <: a) ε :=
  by simp[free, val_bind]; congr; exact funext $ Fin.lastCases (by simp) (by simp)

lemma val_fix (a : M) (t : SyntacticSubTerm L n) :
    t.fix.val s (e <: a) ε = t.val s e (a :>ₙ ε) :=
  by simp[fix, val_bind, Function.comp]; congr; exact funext (Nat.cases (by simp) (by simp))

end Syntactic

end SubTerm