l3lab/ntp-mathlib huggingface dataset:
/-- The linear equivalence `(⨁ i₁, M₁ i₁) ⊗ (⨁ i₂, M₂ i₂) ≃ (⨁ i₁, ⨁ i₂, M₁ i₁ ⊗ M₂ i₂)`, i.e.
"tensor product distributes over direct sum". -/
protected def directSum :
    ((⨁ i₁, M₁ i₁) ⊗[R] ⨁ i₂, M₂ i₂) ≃ₗ[R] ⨁ i : ι₁ × ι₂, M₁ i.1 ⊗[R] M₂ i.2 := by
  -- porting note: entirely rewritten to allow unification to happen one step at a time
  refine LinearEquiv.ofLinear (R := R) (R₂ := R) ?toFun ?invFun ?left ?right
  · refine lift ?_
    refine DirectSum.toModule R _ _ fun i₁ => ?_
    refine LinearMap.flip ?_
    refine DirectSum.toModule R _ _ fun i₂ => LinearMap.flip <| ?_
    refine curry ?_
    exact DirectSum.lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)
  · refine DirectSum.toModule R _ _ fun i => ?_
    exact map (DirectSum.lof R _ M₁ i.1) (DirectSum.lof R _ M₂ i.2)
  · refine DirectSum.linearMap_ext R fun ⟨i₁, i₂⟩ => ?_
    refine TensorProduct.ext ?_
    refine LinearMap.ext₂ fun m₁ m₂ => ?_
    -- porting note: seems much nicer than the `repeat` lean 3 proof.
    

Proof state:
case left
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
x✝ : ι₁ × ι₂
i₁ : ι₁
i₂ : ι₂
m₁ : M₁ (i₁, i₂).1
m₂ : M₂ (i₁, i₂).2
⊢ ((compr₂ (mk R (M₁ (i₁, i₂).1) (M₂ (i₁, i₂).2))
          (comp
              (lift
                (toModule R ι₁ ((⨁ (i₂ : ι₂), M₂ i₂) →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₁ =>
                  LinearMap.flip
                    (toModule R ι₂ (M₁ i₁ →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₂ =>
                      LinearMap.flip (curry (lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂))))))
              (toModule R (ι₁ × ι₂) ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] ⨁ (i₂ : ι₂), M₂ i₂) fun i =>
                map (lof R ι₁ M₁ i.1) (lof R ι₂ M₂ i.2)) ∘ₗ
            lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)))
        m₁)
      m₂ =
    ((compr₂ (mk R (M₁ (i₁, i₂).1) (M₂ (i₁, i₂).2))
          (LinearMap.id ∘ₗ lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)))
        m₁)
      m₂

Next tactic:
simp only [compr₂_apply, comp_apply, id_apply, mk_apply, DirectSum.toModule_lof, map_tmul,
        lift.tmul, flip_apply, curry_apply]
/-- The linear equivalence `(⨁ i₁, M₁ i₁) ⊗ (⨁ i₂, M₂ i₂) ≃ (⨁ i₁, ⨁ i₂, M₁ i₁ ⊗ M₂ i₂)`, i.e.
"tensor product distributes over direct sum". -/
protected def directSum :
    ((⨁ i₁, M₁ i₁) ⊗[R] ⨁ i₂, M₂ i₂) ≃ₗ[R] ⨁ i : ι₁ × ι₂, M₁ i.1 ⊗[R] M₂ i.2 := by
  -- porting note: entirely rewritten to allow unification to happen one step at a time
  refine LinearEquiv.ofLinear (R := R) (R₂ := R) ?toFun ?invFun ?left ?right
  · refine lift ?_
    refine DirectSum.toModule R _ _ fun i₁ => ?_
    refine LinearMap.flip ?_
    refine DirectSum.toModule R _ _ fun i₂ => LinearMap.flip <| ?_
    refine curry ?_
    exact DirectSum.lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)
  · refine DirectSum.toModule R _ _ fun i => ?_
    exact map (DirectSum.lof R _ M₁ i.1) (DirectSum.lof R _ M₂ i.2)
  · refine DirectSum.linearMap_ext R fun ⟨i₁, i₂⟩ => ?_
    refine TensorProduct.ext ?_
    refine LinearMap.ext₂ fun m₁ m₂ => ?_
    -- porting note: seems much nicer than the `repeat` lean 3 proof.
    simp only [compr₂_apply, comp_apply, id_apply, mk_apply, DirectSum.toModule_lof, map_tmul,
        lift.tmul, flip_apply, curry_apply]
  · -- `(_)` prevents typeclass search timing out on problems that can be solved immediately by
    -- unification
    

Proof state:
case right
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
⊢ comp
      (toModule R (ι₁ × ι₂) ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] ⨁ (i₂ : ι₂), M₂ i₂) fun i =>
        map (lof R ι₁ M₁ i.1) (lof R ι₂ M₂ i.2))
      (lift
        (toModule R ι₁ ((⨁ (i₂ : ι₂), M₂ i₂) →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₁ =>
          LinearMap.flip
            (toModule R ι₂ (M₁ i₁ →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₂ =>
              LinearMap.flip (curry (lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)))))) =
    LinearMap.id

Next tactic:
refine TensorProduct.ext ?_
/-- The linear equivalence `(⨁ i₁, M₁ i₁) ⊗ (⨁ i₂, M₂ i₂) ≃ (⨁ i₁, ⨁ i₂, M₁ i₁ ⊗ M₂ i₂)`, i.e.
"tensor product distributes over direct sum". -/
protected def directSum :
    ((⨁ i₁, M₁ i₁) ⊗[R] ⨁ i₂, M₂ i₂) ≃ₗ[R] ⨁ i : ι₁ × ι₂, M₁ i.1 ⊗[R] M₂ i.2 := by
  -- porting note: entirely rewritten to allow unification to happen one step at a time
  refine LinearEquiv.ofLinear (R := R) (R₂ := R) ?toFun ?invFun ?left ?right
  · refine lift ?_
    refine DirectSum.toModule R _ _ fun i₁ => ?_
    refine LinearMap.flip ?_
    refine DirectSum.toModule R _ _ fun i₂ => LinearMap.flip <| ?_
    refine curry ?_
    exact DirectSum.lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)
  · refine DirectSum.toModule R _ _ fun i => ?_
    exact map (DirectSum.lof R _ M₁ i.1) (DirectSum.lof R _ M₂ i.2)
  · refine DirectSum.linearMap_ext R fun ⟨i₁, i₂⟩ => ?_
    refine TensorProduct.ext ?_
    refine LinearMap.ext₂ fun m₁ m₂ => ?_
    -- porting note: seems much nicer than the `repeat` lean 3 proof.
    simp only [compr₂_apply, comp_apply, id_apply, mk_apply, DirectSum.toModule_lof, map_tmul,
        lift.tmul, flip_apply, curry_apply]
  · -- `(_)` prevents typeclass search timing out on problems that can be solved immediately by
    -- unification
    refine TensorProduct.ext ?_
    

Proof state:
case right
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
⊢ compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂))
      (comp
        (toModule R (ι₁ × ι₂) ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] ⨁ (i₂ : ι₂), M₂ i₂) fun i =>
          map (lof R ι₁ M₁ i.1) (lof R ι₂ M₂ i.2))
        (lift
          (toModule R ι₁ ((⨁ (i₂ : ι₂), M₂ i₂) →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₁ =>
            LinearMap.flip
              (toModule R ι₂ (M₁ i₁ →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₂ =>
                LinearMap.flip (curry (lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂))))))) =
    compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂)) LinearMap.id

Next tactic:
refine DirectSum.linearMap_ext _ fun i₁ => ?_
/-- The linear equivalence `(⨁ i₁, M₁ i₁) ⊗ (⨁ i₂, M₂ i₂) ≃ (⨁ i₁, ⨁ i₂, M₁ i₁ ⊗ M₂ i₂)`, i.e.
"tensor product distributes over direct sum". -/
protected def directSum :
    ((⨁ i₁, M₁ i₁) ⊗[R] ⨁ i₂, M₂ i₂) ≃ₗ[R] ⨁ i : ι₁ × ι₂, M₁ i.1 ⊗[R] M₂ i.2 := by
  -- porting note: entirely rewritten to allow unification to happen one step at a time
  refine LinearEquiv.ofLinear (R := R) (R₂ := R) ?toFun ?invFun ?left ?right
  · refine lift ?_
    refine DirectSum.toModule R _ _ fun i₁ => ?_
    refine LinearMap.flip ?_
    refine DirectSum.toModule R _ _ fun i₂ => LinearMap.flip <| ?_
    refine curry ?_
    exact DirectSum.lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)
  · refine DirectSum.toModule R _ _ fun i => ?_
    exact map (DirectSum.lof R _ M₁ i.1) (DirectSum.lof R _ M₂ i.2)
  · refine DirectSum.linearMap_ext R fun ⟨i₁, i₂⟩ => ?_
    refine TensorProduct.ext ?_
    refine LinearMap.ext₂ fun m₁ m₂ => ?_
    -- porting note: seems much nicer than the `repeat` lean 3 proof.
    simp only [compr₂_apply, comp_apply, id_apply, mk_apply, DirectSum.toModule_lof, map_tmul,
        lift.tmul, flip_apply, curry_apply]
  · -- `(_)` prevents typeclass search timing out on problems that can be solved immediately by
    -- unification
    refine TensorProduct.ext ?_
    refine DirectSum.linearMap_ext _ fun i₁ => ?_
    

Proof state:
case right
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
i₁ : ι₁
⊢ compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂))
        (comp
          (toModule R (ι₁ × ι₂) ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] ⨁ (i₂ : ι₂), M₂ i₂) fun i =>
            map (lof R ι₁ M₁ i.1) (lof R ι₂ M₂ i.2))
          (lift
            (toModule R ι₁ ((⨁ (i₂ : ι₂), M₂ i₂) →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₁ =>
              LinearMap.flip
                (toModule R ι₂ (M₁ i₁ →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₂ =>
                  LinearMap.flip (curry (lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂))))))) ∘ₗ
      lof R ι₁ (fun i => M₁ i) i₁ =
    compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂)) LinearMap.id ∘ₗ lof R ι₁ (fun i => M₁ i) i₁

Next tactic:
refine LinearMap.ext fun x₁ => ?_
/-- The linear equivalence `(⨁ i₁, M₁ i₁) ⊗ (⨁ i₂, M₂ i₂) ≃ (⨁ i₁, ⨁ i₂, M₁ i₁ ⊗ M₂ i₂)`, i.e.
"tensor product distributes over direct sum". -/
protected def directSum :
    ((⨁ i₁, M₁ i₁) ⊗[R] ⨁ i₂, M₂ i₂) ≃ₗ[R] ⨁ i : ι₁ × ι₂, M₁ i.1 ⊗[R] M₂ i.2 := by
  -- porting note: entirely rewritten to allow unification to happen one step at a time
  refine LinearEquiv.ofLinear (R := R) (R₂ := R) ?toFun ?invFun ?left ?right
  · refine lift ?_
    refine DirectSum.toModule R _ _ fun i₁ => ?_
    refine LinearMap.flip ?_
    refine DirectSum.toModule R _ _ fun i₂ => LinearMap.flip <| ?_
    refine curry ?_
    exact DirectSum.lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)
  · refine DirectSum.toModule R _ _ fun i => ?_
    exact map (DirectSum.lof R _ M₁ i.1) (DirectSum.lof R _ M₂ i.2)
  · refine DirectSum.linearMap_ext R fun ⟨i₁, i₂⟩ => ?_
    refine TensorProduct.ext ?_
    refine LinearMap.ext₂ fun m₁ m₂ => ?_
    -- porting note: seems much nicer than the `repeat` lean 3 proof.
    simp only [compr₂_apply, comp_apply, id_apply, mk_apply, DirectSum.toModule_lof, map_tmul,
        lift.tmul, flip_apply, curry_apply]
  · -- `(_)` prevents typeclass search timing out on problems that can be solved immediately by
    -- unification
    refine TensorProduct.ext ?_
    refine DirectSum.linearMap_ext _ fun i₁ => ?_
    refine LinearMap.ext fun x₁ => ?_
    

Proof state:
case right
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
i₁ : ι₁
x₁ : M₁ i₁
⊢ (compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂))
          (comp
            (toModule R (ι₁ × ι₂) ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] ⨁ (i₂ : ι₂), M₂ i₂) fun i =>
              map (lof R ι₁ M₁ i.1) (lof R ι₂ M₂ i.2))
            (lift
              (toModule R ι₁ ((⨁ (i₂ : ι₂), M₂ i₂) →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₁ =>
                LinearMap.flip
                  (toModule R ι₂ (M₁ i₁ →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₂ =>
                    LinearMap.flip (curry (lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂))))))) ∘ₗ
        lof R ι₁ (fun i => M₁ i) i₁)
      x₁ =
    (compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂)) LinearMap.id ∘ₗ lof R ι₁ (fun i => M₁ i) i₁) x₁

Next tactic:
refine DirectSum.linearMap_ext _ fun i₂ => ?_
/-- The linear equivalence `(⨁ i₁, M₁ i₁) ⊗ (⨁ i₂, M₂ i₂) ≃ (⨁ i₁, ⨁ i₂, M₁ i₁ ⊗ M₂ i₂)`, i.e.
"tensor product distributes over direct sum". -/
protected def directSum :
    ((⨁ i₁, M₁ i₁) ⊗[R] ⨁ i₂, M₂ i₂) ≃ₗ[R] ⨁ i : ι₁ × ι₂, M₁ i.1 ⊗[R] M₂ i.2 := by
  -- porting note: entirely rewritten to allow unification to happen one step at a time
  refine LinearEquiv.ofLinear (R := R) (R₂ := R) ?toFun ?invFun ?left ?right
  · refine lift ?_
    refine DirectSum.toModule R _ _ fun i₁ => ?_
    refine LinearMap.flip ?_
    refine DirectSum.toModule R _ _ fun i₂ => LinearMap.flip <| ?_
    refine curry ?_
    exact DirectSum.lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)
  · refine DirectSum.toModule R _ _ fun i => ?_
    exact map (DirectSum.lof R _ M₁ i.1) (DirectSum.lof R _ M₂ i.2)
  · refine DirectSum.linearMap_ext R fun ⟨i₁, i₂⟩ => ?_
    refine TensorProduct.ext ?_
    refine LinearMap.ext₂ fun m₁ m₂ => ?_
    -- porting note: seems much nicer than the `repeat` lean 3 proof.
    simp only [compr₂_apply, comp_apply, id_apply, mk_apply, DirectSum.toModule_lof, map_tmul,
        lift.tmul, flip_apply, curry_apply]
  · -- `(_)` prevents typeclass search timing out on problems that can be solved immediately by
    -- unification
    refine TensorProduct.ext ?_
    refine DirectSum.linearMap_ext _ fun i₁ => ?_
    refine LinearMap.ext fun x₁ => ?_
    refine DirectSum.linearMap_ext _ fun i₂ => ?_
    

Proof state:
case right
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
i₁ : ι₁
x₁ : M₁ i₁
i₂ : ι₂
⊢ (compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂))
            (comp
              (toModule R (ι₁ × ι₂) ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] ⨁ (i₂ : ι₂), M₂ i₂) fun i =>
                map (lof R ι₁ M₁ i.1) (lof R ι₂ M₂ i.2))
              (lift
                (toModule R ι₁ ((⨁ (i₂ : ι₂), M₂ i₂) →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₁ =>
                  LinearMap.flip
                    (toModule R ι₂ (M₁ i₁ →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₂ =>
                      LinearMap.flip (curry (lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂))))))) ∘ₗ
          lof R ι₁ (fun i => M₁ i) i₁)
        x₁ ∘ₗ
      lof R ι₂ (fun i => M₂ i) i₂ =
    (compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂)) LinearMap.id ∘ₗ lof R ι₁ (fun i => M₁ i) i₁) x₁ ∘ₗ
      lof R ι₂ (fun i => M₂ i) i₂

Next tactic:
refine LinearMap.ext fun x₂ => ?_
/-- The linear equivalence `(⨁ i₁, M₁ i₁) ⊗ (⨁ i₂, M₂ i₂) ≃ (⨁ i₁, ⨁ i₂, M₁ i₁ ⊗ M₂ i₂)`, i.e.
"tensor product distributes over direct sum". -/
protected def directSum :
    ((⨁ i₁, M₁ i₁) ⊗[R] ⨁ i₂, M₂ i₂) ≃ₗ[R] ⨁ i : ι₁ × ι₂, M₁ i.1 ⊗[R] M₂ i.2 := by
  -- porting note: entirely rewritten to allow unification to happen one step at a time
  refine LinearEquiv.ofLinear (R := R) (R₂ := R) ?toFun ?invFun ?left ?right
  · refine lift ?_
    refine DirectSum.toModule R _ _ fun i₁ => ?_
    refine LinearMap.flip ?_
    refine DirectSum.toModule R _ _ fun i₂ => LinearMap.flip <| ?_
    refine curry ?_
    exact DirectSum.lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂)
  · refine DirectSum.toModule R _ _ fun i => ?_
    exact map (DirectSum.lof R _ M₁ i.1) (DirectSum.lof R _ M₂ i.2)
  · refine DirectSum.linearMap_ext R fun ⟨i₁, i₂⟩ => ?_
    refine TensorProduct.ext ?_
    refine LinearMap.ext₂ fun m₁ m₂ => ?_
    -- porting note: seems much nicer than the `repeat` lean 3 proof.
    simp only [compr₂_apply, comp_apply, id_apply, mk_apply, DirectSum.toModule_lof, map_tmul,
        lift.tmul, flip_apply, curry_apply]
  · -- `(_)` prevents typeclass search timing out on problems that can be solved immediately by
    -- unification
    refine TensorProduct.ext ?_
    refine DirectSum.linearMap_ext _ fun i₁ => ?_
    refine LinearMap.ext fun x₁ => ?_
    refine DirectSum.linearMap_ext _ fun i₂ => ?_
    refine LinearMap.ext fun x₂ => ?_
    -- porting note: seems much nicer than the `repeat` lean 3 proof.
    

Proof state:
case right
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
i₁ : ι₁
x₁ : M₁ i₁
i₂ : ι₂
x₂ : M₂ i₂
⊢ ((compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂))
              (comp
                (toModule R (ι₁ × ι₂) ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] ⨁ (i₂ : ι₂), M₂ i₂) fun i =>
                  map (lof R ι₁ M₁ i.1) (lof R ι₂ M₂ i.2))
                (lift
                  (toModule R ι₁ ((⨁ (i₂ : ι₂), M₂ i₂) →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₁ =>
                    LinearMap.flip
                      (toModule R ι₂ (M₁ i₁ →ₗ[R] ⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) fun i₂ =>
                        LinearMap.flip (curry (lof R (ι₁ × ι₂) (fun i => M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂))))))) ∘ₗ
            lof R ι₁ (fun i => M₁ i) i₁)
          x₁ ∘ₗ
        lof R ι₂ (fun i => M₂ i) i₂)
      x₂ =
    ((compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) (⨁ (i₂ : ι₂), M₂ i₂)) LinearMap.id ∘ₗ lof R ι₁ (fun i => M₁ i) i₁) x₁ ∘ₗ
        lof R ι₂ (fun i => M₂ i) i₂)
      x₂

Next tactic:
simp only [compr₂_apply, comp_apply, id_apply, mk_apply, DirectSum.toModule_lof, map_tmul,
        lift.tmul, flip_apply, curry_apply]
/-- Tensor products distribute over a direct sum on the left . -/
def directSumLeft : (⨁ i₁, M₁ i₁) ⊗[R] M₂' ≃ₗ[R] ⨁ i, M₁ i ⊗[R] M₂' :=
  LinearEquiv.ofLinear
    (lift <|
      DirectSum.toModule R _ _ fun i =>
        (mk R _ _).compr₂ <| DirectSum.lof R ι₁ (fun i => M₁ i ⊗[R] M₂') _)
    (DirectSum.toModule R _ _ fun i => rTensor _ (DirectSum.lof R ι₁ _ _))
    (DirectSum.linearMap_ext R fun i =>
      TensorProduct.ext <|
        LinearMap.ext₂ fun m₁ m₂ => by
          

Proof state:
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
i : ι₁
m₁ : M₁ i
m₂ : M₂'
⊢ ((compr₂ (mk R (M₁ i) M₂')
          (comp
              (lift
                (toModule R ι₁ (M₂' →ₗ[R] ⨁ (i : ι₁), M₁ i ⊗[R] M₂') fun i =>
                  compr₂ (mk R (M₁ i) M₂') (lof R ι₁ (fun i => M₁ i ⊗[R] M₂') i)))
              (toModule R ι₁ ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] M₂') fun i => rTensor M₂' (lof R ι₁ M₁ i)) ∘ₗ
            lof R ι₁ (fun i => M₁ i ⊗[R] M₂') i))
        m₁)
      m₂ =
    ((compr₂ (mk R (M₁ i) M₂') (LinearMap.id ∘ₗ lof R ι₁ (fun i => M₁ i ⊗[R] M₂') i)) m₁) m₂

Next tactic:
dsimp only [comp_apply, compr₂_apply, id_apply, mk_apply]
/-- Tensor products distribute over a direct sum on the left . -/
def directSumLeft : (⨁ i₁, M₁ i₁) ⊗[R] M₂' ≃ₗ[R] ⨁ i, M₁ i ⊗[R] M₂' :=
  LinearEquiv.ofLinear
    (lift <|
      DirectSum.toModule R _ _ fun i =>
        (mk R _ _).compr₂ <| DirectSum.lof R ι₁ (fun i => M₁ i ⊗[R] M₂') _)
    (DirectSum.toModule R _ _ fun i => rTensor _ (DirectSum.lof R ι₁ _ _))
    (DirectSum.linearMap_ext R fun i =>
      TensorProduct.ext <|
        LinearMap.ext₂ fun m₁ m₂ => by
          dsimp only [comp_apply, compr₂_apply, id_apply, mk_apply]
          

Proof state:
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
i : ι₁
m₁ : M₁ i
m₂ : M₂'
⊢ (lift
        (toModule R ι₁ (M₂' →ₗ[R] ⨁ (i : ι₁), M₁ i ⊗[R] M₂') fun i =>
          compr₂ (mk R (M₁ i) M₂') (lof R ι₁ (fun i => M₁ i ⊗[R] M₂') i)))
      ((toModule R ι₁ ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] M₂') fun i => rTensor M₂' (lof R ι₁ M₁ i))
        ((lof R ι₁ (fun i => M₁ i ⊗[R] M₂') i) (m₁ ⊗ₜ[R] m₂))) =
    (lof R ι₁ (fun i => M₁ i ⊗[R] M₂') i) (m₁ ⊗ₜ[R] m₂)

Next tactic:
simp_rw [DirectSum.toModule_lof, rTensor_tmul, lift.tmul, DirectSum.toModule_lof,
            compr₂_apply, mk_apply]
/-- Tensor products distribute over a direct sum on the left . -/
def directSumLeft : (⨁ i₁, M₁ i₁) ⊗[R] M₂' ≃ₗ[R] ⨁ i, M₁ i ⊗[R] M₂' :=
  LinearEquiv.ofLinear
    (lift <|
      DirectSum.toModule R _ _ fun i =>
        (mk R _ _).compr₂ <| DirectSum.lof R ι₁ (fun i => M₁ i ⊗[R] M₂') _)
    (DirectSum.toModule R _ _ fun i => rTensor _ (DirectSum.lof R ι₁ _ _))
    (DirectSum.linearMap_ext R fun i =>
      TensorProduct.ext <|
        LinearMap.ext₂ fun m₁ m₂ => by
          dsimp only [comp_apply, compr₂_apply, id_apply, mk_apply]
          simp_rw [DirectSum.toModule_lof, rTensor_tmul, lift.tmul, DirectSum.toModule_lof,
            compr₂_apply, mk_apply])
    (TensorProduct.ext <|
      DirectSum.linearMap_ext R fun i =>
        LinearMap.ext₂ fun m₁ m₂ => by
          

Proof state:
R : Type u
inst✝¹⁰ : CommRing R
ι₁ : Type v₁
ι₂ : Type v₂
inst✝⁹ : DecidableEq ι₁
inst✝⁸ : DecidableEq ι₂
M₁ : ι₁ → Type w₁
M₁' : Type w₁'
M₂ : ι₂ → Type w₂
M₂' : Type w₂'
inst✝⁷ : (i₁ : ι₁) → AddCommGroup (M₁ i₁)
inst✝⁶ : AddCommGroup M₁'
inst✝⁵ : (i₂ : ι₂) → AddCommGroup (M₂ i₂)
inst✝⁴ : AddCommGroup M₂'
inst✝³ : (i₁ : ι₁) → Module R (M₁ i₁)
inst✝² : Module R M₁'
inst✝¹ : (i₂ : ι₂) → Module R (M₂ i₂)
inst✝ : Module R M₂'
i : ι₁
m₁ : M₁ i
m₂ : M₂'
⊢ ((compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) M₂')
            (comp (toModule R ι₁ ((⨁ (i₁ : ι₁), M₁ i₁) ⊗[R] M₂') fun i => rTensor M₂' (lof R ι₁ M₁ i))
              (lift
                (toModule R ι₁ (M₂' →ₗ[R] ⨁ (i : ι₁), M₁ i ⊗[R] M₂') fun i =>
                  compr₂ (mk R (M₁ i) M₂') (lof R ι₁ (fun i => M₁ i ⊗[R] M₂') i)))) ∘ₗ
          lof R ι₁ (fun i => M₁ i) i)
        m₁)
      m₂ =
    ((compr₂ (mk R (⨁ (i₁ : ι₁), M₁ i₁) M₂') LinearMap.id ∘ₗ lof R ι₁ (fun i => M₁ i) i) m₁) m₂

Next tactic:
dsimp only [comp_apply, compr₂_apply, id_apply, mk_apply]