l3lab/ntp-mathlib huggingface dataset:
theorem fast_fib_aux_eq (n : ℕ) : fastFibAux n = (fib n, fib (n + 1)) := by
  apply Nat.binaryRec _ (fun b n' ih => _) n
  · simp [fastFibAux]
  · intro b
    intro n'
    intro ih
    cases b <;>
          simp only [fast_fib_aux_bit_ff, fast_fib_aux_bit_tt, congr_arg Prod.fst ih,
            congr_arg Prod.snd ih, Prod.mk.inj_iff] <;>
          

Proof state:
case true
n n' : ℕ
ih : fastFibAux n' = (fib n', fib (n' + 1))
⊢ fib (n' + 1) ^ 2 + fib n' ^ 2 = fib (bit true n') ∧ fib (n' + 1) * (2 * fib n' + fib (n' + 1)) = fib (bit true n' + 1)

Next tactic:
simp [bit, fib_bit0, fib_bit1, fib_bit0_succ, fib_bit1_succ]
theorem fast_fib_eq (n : ℕ) : fastFib n = fib n := by 

Proof state:
n : ℕ
⊢ fastFib n = fib n

Next tactic:
rw [fastFib, fast_fib_aux_eq]
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  

Proof state:
m n : ℕ
⊢ gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n)

Next tactic:
rcases Nat.eq_zero_or_pos n with h | h
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  rcases Nat.eq_zero_or_pos n with h | h
  · 

Proof state:
case inl
m n : ℕ
h : n = 0
⊢ gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n)

Next tactic:
rw [h]
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  rcases Nat.eq_zero_or_pos n with h | h
  · rw [h]
    

Proof state:
case inl
m n : ℕ
h : n = 0
⊢ gcd (fib m) (fib (0 + m)) = gcd (fib m) (fib 0)

Next tactic:
simp
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  rcases Nat.eq_zero_or_pos n with h | h
  · rw [h]
    simp
  

Proof state:
case inr
m n : ℕ
h : n > 0
⊢ gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n)

Next tactic:
replace h := Nat.succ_pred_eq_of_pos h
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  rcases Nat.eq_zero_or_pos n with h | h
  · rw [h]
    simp
  replace h := Nat.succ_pred_eq_of_pos h; 

Proof state:
case inr
m n : ℕ
h : succ (pred n) = n
⊢ gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n)

Next tactic:
rw [← h, succ_eq_add_one]
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  rcases Nat.eq_zero_or_pos n with h | h
  · rw [h]
    simp
  replace h := Nat.succ_pred_eq_of_pos h; rw [← h, succ_eq_add_one]
  

Proof state:
case inr
m n : ℕ
h : succ (pred n) = n
⊢ gcd (fib m) (fib (pred n + 1 + m)) = gcd (fib m) (fib (pred n + 1))

Next tactic:
calc
    gcd (fib m) (fib (n.pred + 1 + m)) =
        gcd (fib m) (fib n.pred * fib m + fib (n.pred + 1) * fib (m + 1)) := by
        rw [← fib_add n.pred _]
        ring_nf
    _ = gcd (fib m) (fib (n.pred + 1) * fib (m + 1)) := by
        rw [add_comm, gcd_add_mul_right_right (fib m) _ (fib n.pred)]
    _ = gcd (fib m) (fib (n.pred + 1)) :=
      Coprime.gcd_mul_right_cancel_right (fib (n.pred + 1)) (Coprime.symm (fib_coprime_fib_succ m))
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  rcases Nat.eq_zero_or_pos n with h | h
  · rw [h]
    simp
  replace h := Nat.succ_pred_eq_of_pos h; rw [← h, succ_eq_add_one]
  calc
    gcd (fib m) (fib (n.pred + 1 + m)) =
        gcd (fib m) (fib n.pred * fib m + fib (n.pred + 1) * fib (m + 1)) := by
        

Proof state:
m n : ℕ
h : succ (pred n) = n
⊢ gcd (fib m) (fib (pred n + 1 + m)) = gcd (fib m) (fib (pred n) * fib m + fib (pred n + 1) * fib (m + 1))

Next tactic:
rw [← fib_add n.pred _]
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  rcases Nat.eq_zero_or_pos n with h | h
  · rw [h]
    simp
  replace h := Nat.succ_pred_eq_of_pos h; rw [← h, succ_eq_add_one]
  calc
    gcd (fib m) (fib (n.pred + 1 + m)) =
        gcd (fib m) (fib n.pred * fib m + fib (n.pred + 1) * fib (m + 1)) := by
        rw [← fib_add n.pred _]
        

Proof state:
m n : ℕ
h : succ (pred n) = n
⊢ gcd (fib m) (fib (pred n + 1 + m)) = gcd (fib m) (fib (pred n + m + 1))

Next tactic:
ring_nf
theorem gcd_fib_add_self (m n : ℕ) : gcd (fib m) (fib (n + m)) = gcd (fib m) (fib n) := by
  rcases Nat.eq_zero_or_pos n with h | h
  · rw [h]
    simp
  replace h := Nat.succ_pred_eq_of_pos h; rw [← h, succ_eq_add_one]
  calc
    gcd (fib m) (fib (n.pred + 1 + m)) =
        gcd (fib m) (fib n.pred * fib m + fib (n.pred + 1) * fib (m + 1)) := by
        rw [← fib_add n.pred _]
        ring_nf
    _ = gcd (fib m) (fib (n.pred + 1) * fib (m + 1)) := by
        

Proof state:
m n : ℕ
h : succ (pred n) = n
⊢ gcd (fib m) (fib (pred n) * fib m + fib (pred n + 1) * fib (m + 1)) = gcd (fib m) (fib (pred n + 1) * fib (m + 1))

Next tactic:
rw [add_comm, gcd_add_mul_right_right (fib m) _ (fib n.pred)]
theorem gcd_fib_add_mul_self (m n : ℕ) : ∀ k, gcd (fib m) (fib (n + k * m)) = gcd (fib m) (fib n)
  | 0 => by 

Proof state:
m n : ℕ
⊢ gcd (fib m) (fib (n + 0 * m)) = gcd (fib m) (fib n)

Next tactic:
simp
theorem gcd_fib_add_mul_self (m n : ℕ) : ∀ k, gcd (fib m) (fib (n + k * m)) = gcd (fib m) (fib n)
  | 0 => by simp
  | k + 1 => by
    

Proof state:
m n k : ℕ
⊢ gcd (fib m) (fib (n + (k + 1) * m)) = gcd (fib m) (fib n)

Next tactic:
rw [← gcd_fib_add_mul_self m n k,
      add_mul,
      ← add_assoc,
      one_mul,
      gcd_fib_add_self _ _]
/-- `fib n` is a strong divisibility sequence,
  see https://proofwiki.org/wiki/GCD_of_Fibonacci_Numbers -/
theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by
  

Proof state:
m n : ℕ
⊢ fib (gcd m n) = gcd (fib m) (fib n)

Next tactic:
induction m, n using Nat.gcd.induction with
  | H0 => simp
  | H1 m n _ h' =>
    rw [← gcd_rec m n] at h'
    conv_rhs => rw [← mod_add_div' n m]
    rwa [gcd_fib_add_mul_self m (n % m) (n / m), gcd_comm (fib m) _]
/-- `fib n` is a strong divisibility sequence,
  see https://proofwiki.org/wiki/GCD_of_Fibonacci_Numbers -/
theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by
  induction m, n using Nat.gcd.induction with
  

Proof state:
case H0
n✝ : ℕ
⊢ fib (gcd 0 n✝) = gcd (fib 0) (fib n✝)

Next tactic:
| H0 => simp
/-- `fib n` is a strong divisibility sequence,
  see https://proofwiki.org/wiki/GCD_of_Fibonacci_Numbers -/
theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by
  induction m, n using Nat.gcd.induction with
  | H0 => 

Proof state:
case H0
n✝ : ℕ
⊢ fib (gcd 0 n✝) = gcd (fib 0) (fib n✝)

Next tactic:
simp
/-- `fib n` is a strong divisibility sequence,
  see https://proofwiki.org/wiki/GCD_of_Fibonacci_Numbers -/
theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by
  induction m, n using Nat.gcd.induction with
  | H0 => simp
  

Proof state:
case H1
m n : ℕ
a✝ : 0 < m
h' : fib (gcd (n % m) m) = gcd (fib (n % m)) (fib m)
⊢ fib (gcd m n) = gcd (fib m) (fib n)

Next tactic:
| H1 m n _ h' =>
    rw [← gcd_rec m n] at h'
    conv_rhs => rw [← mod_add_div' n m]
    rwa [gcd_fib_add_mul_self m (n % m) (n / m), gcd_comm (fib m) _]
/-- `fib n` is a strong divisibility sequence,
  see https://proofwiki.org/wiki/GCD_of_Fibonacci_Numbers -/
theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by
  induction m, n using Nat.gcd.induction with
  | H0 => simp
  | H1 m n _ h' =>
    

Proof state:
case H1
m n : ℕ
a✝ : 0 < m
h' : fib (gcd (n % m) m) = gcd (fib (n % m)) (fib m)
⊢ fib (gcd m n) = gcd (fib m) (fib n)

Next tactic:
rw [← gcd_rec m n] at h'
/-- `fib n` is a strong divisibility sequence,
  see https://proofwiki.org/wiki/GCD_of_Fibonacci_Numbers -/
theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by
  induction m, n using Nat.gcd.induction with
  | H0 => simp
  | H1 m n _ h' =>
    rw [← gcd_rec m n] at h'
    

Proof state:
case H1
m n : ℕ
a✝ : 0 < m
h' : fib (gcd m n) = gcd (fib (n % m)) (fib m)
⊢ fib (gcd m n) = gcd (fib m) (fib n)

Next tactic:
conv_rhs => rw [← mod_add_div' n m]
/-- `fib n` is a strong divisibility sequence,
  see https://proofwiki.org/wiki/GCD_of_Fibonacci_Numbers -/
theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by
  induction m, n using Nat.gcd.induction with
  | H0 => simp
  | H1 m n _ h' =>
    rw [← gcd_rec m n] at h'
    conv_rhs => 

Proof state:
m n : ℕ
a✝ : 0 < m
h' : fib (gcd m n) = gcd (fib (n % m)) (fib m)
| gcd (fib m) (fib n)

Next tactic:
rw [← mod_add_div' n m]
/-- `fib n` is a strong divisibility sequence,
  see https://proofwiki.org/wiki/GCD_of_Fibonacci_Numbers -/
theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by
  induction m, n using Nat.gcd.induction with
  | H0 => simp
  | H1 m n _ h' =>
    rw [← gcd_rec m n] at h'
    conv_rhs => rw [← mod_add_div' n m]
    

Proof state:
case H1
m n : ℕ
a✝ : 0 < m
h' : fib (gcd m n) = gcd (fib (n % m)) (fib m)
⊢ fib (gcd m n) = gcd (fib m) (fib (n % m + n / m * m))

Next tactic:
rwa [gcd_fib_add_mul_self m (n % m) (n / m), gcd_comm (fib m) _]
theorem fib_dvd (m n : ℕ) (h : m ∣ n) : fib m ∣ fib n := by
  

Proof state:
m n : ℕ
h : m ∣ n
⊢ fib m ∣ fib n

Next tactic:
rwa [gcd_eq_left_iff_dvd, ← fib_gcd, gcd_eq_left_iff_dvd.mp]
theorem fib_succ_eq_sum_choose :
    ∀ n : ℕ, fib (n + 1) = ∑ p in Finset.antidiagonal n, choose p.1 p.2 :=
  twoStepInduction rfl rfl fun n h1 h2 => by
    

Proof state:
n : ℕ
h1 : fib (n + 1) = ∑ p in Finset.antidiagonal n, choose p.1 p.2
h2 : fib (succ n + 1) = ∑ p in Finset.antidiagonal (succ n), choose p.1 p.2
⊢ fib (succ (succ n) + 1) = ∑ p in Finset.antidiagonal (succ (succ n)), choose p.1 p.2

Next tactic:
rw [fib_add_two, h1, h2, Finset.Nat.antidiagonal_succ_succ', Finset.Nat.antidiagonal_succ']
theorem fib_succ_eq_sum_choose :
    ∀ n : ℕ, fib (n + 1) = ∑ p in Finset.antidiagonal n, choose p.1 p.2 :=
  twoStepInduction rfl rfl fun n h1 h2 => by
    rw [fib_add_two, h1, h2, Finset.Nat.antidiagonal_succ_succ', Finset.Nat.antidiagonal_succ']
    

Proof state:
n : ℕ
h1 : fib (n + 1) = ∑ p in Finset.antidiagonal n, choose p.1 p.2
h2 : fib (succ n + 1) = ∑ p in Finset.antidiagonal (succ n), choose p.1 p.2
⊢ ∑ p in Finset.antidiagonal n, choose p.1 p.2 +
      ∑ p in
        Finset.cons (n + 1, 0)
          (Finset.map (Function.Embedding.prodMap (Function.Embedding.refl ℕ) { toFun := succ, inj' := succ_injective })
            (Finset.antidiagonal n))
          (_ :
            (n + 1, 0) ∉
              Finset.map
                (Function.Embedding.prodMap (Function.Embedding.refl ℕ) { toFun := succ, inj' := succ_injective })
                (Finset.antidiagonal n)),
        choose p.1 p.2 =
    ∑ p in
      Finset.cons (0, n + 2)
        (Finset.cons (n + 2, 0)
          (Finset.map
            (Function.Embedding.prodMap { toFun := succ, inj' := succ_injective }
              { toFun := succ, inj' := succ_injective })
            (Finset.antidiagonal n))
          (_ :
            (n + 2, 0) ∉
              Finset.map
                (Function.Embedding.prodMap { toFun := succ, inj' := succ_injective }
                  { toFun := succ, inj' := succ_injective })
                (Finset.antidiagonal n)))
        (_ :
          (0, n + 2) ∉
            Finset.cons (n + 2, 0)
              (Finset.map
                (Function.Embedding.prodMap { toFun := succ, inj' := succ_injective }
                  { toFun := succ, inj' := succ_injective })
                (Finset.antidiagonal n))
              (_ :
                (n + 2, 0) ∉
                  Finset.map
                    (Function.Embedding.prodMap { toFun := succ, inj' := succ_injective }
                      { toFun := succ, inj' := succ_injective })
                    (Finset.antidiagonal n))),
      choose p.1 p.2

Next tactic:
simp [choose_succ_succ, Finset.sum_add_distrib, add_left_comm]
theorem fib_succ_eq_succ_sum (n : ℕ) : fib (n + 1) = (∑ k in Finset.range n, fib k) + 1 := by
  

Proof state:
n : ℕ
⊢ fib (n + 1) = ∑ k in Finset.range n, fib k + 1

Next tactic:
induction' n with n ih
theorem fib_succ_eq_succ_sum (n : ℕ) : fib (n + 1) = (∑ k in Finset.range n, fib k) + 1 := by
  induction' n with n ih
  · 

Proof state:
case zero
⊢ fib (zero + 1) = ∑ k in Finset.range zero, fib k + 1

Next tactic:
simp
theorem fib_succ_eq_succ_sum (n : ℕ) : fib (n + 1) = (∑ k in Finset.range n, fib k) + 1 := by
  induction' n with n ih
  · simp
  · 

Proof state:
case succ
n : ℕ
ih : fib (n + 1) = ∑ k in Finset.range n, fib k + 1
⊢ fib (succ n + 1) = ∑ k in Finset.range (succ n), fib k + 1

Next tactic:
calc
      fib (n + 2) = fib n + fib (n + 1) := fib_add_two
      _ = (fib n + ∑ k in Finset.range n, fib k) + 1 := by rw [ih, add_assoc]
      _ = (∑ k in Finset.range (n + 1), fib k) + 1 := by simp [Finset.range_add_one]
theorem fib_succ_eq_succ_sum (n : ℕ) : fib (n + 1) = (∑ k in Finset.range n, fib k) + 1 := by
  induction' n with n ih
  · simp
  · calc
      fib (n + 2) = fib n + fib (n + 1) := fib_add_two
      _ = (fib n + ∑ k in Finset.range n, fib k) + 1 := by 

Proof state:
n : ℕ
ih : fib (n + 1) = ∑ k in Finset.range n, fib k + 1
⊢ fib n + fib (n + 1) = fib n + ∑ k in Finset.range n, fib k + 1

Next tactic:
rw [ih, add_assoc]
theorem fib_succ_eq_succ_sum (n : ℕ) : fib (n + 1) = (∑ k in Finset.range n, fib k) + 1 := by
  induction' n with n ih
  · simp
  · calc
      fib (n + 2) = fib n + fib (n + 1) := fib_add_two
      _ = (fib n + ∑ k in Finset.range n, fib k) + 1 := by rw [ih, add_assoc]
      _ = (∑ k in Finset.range (n + 1), fib k) + 1 := by 

Proof state:
n : ℕ
ih : fib (n + 1) = ∑ k in Finset.range n, fib k + 1
⊢ fib n + ∑ k in Finset.range n, fib k + 1 = ∑ k in Finset.range (n + 1), fib k + 1

Next tactic:
simp [Finset.range_add_one]
/-- `MulOpposite` distributes over `TensorProduct`. Note this is an `S`-algebra morphism, where
`A/S/R` is a tower of algebras. -/
def opAlgEquiv : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₐ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  letI e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr
      (opLinearEquiv S).symm (opLinearEquiv R).symm ≪≫ₗ opLinearEquiv S
  letI e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
  AlgEquiv.ofAlgHom
    (algHomOfLinearMapTensorProduct e₁.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.opComm <| algHomOfLinearMapTensorProduct e₂.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.op.symm.injective <| by 

Proof state:
R : Type u_1
S : Type u_2
A : Type u_3
B : Type u_4
inst✝⁸ : CommSemiring R
inst✝⁷ : CommSemiring S
inst✝⁶ : Semiring A
inst✝⁵ : Semiring B
inst✝⁴ : Algebra R S
inst✝³ : Algebra R A
inst✝² : Algebra R B
inst✝¹ : Algebra S A
inst✝ : IsScalarTower R S A
e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (LinearEquiv.symm (opLinearEquiv S)) (LinearEquiv.symm (opLinearEquiv R)) ≪≫ₗ
    opLinearEquiv S
e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
⊢ AlgHom.op.symm
      (AlgHom.comp
        (algHomOfLinearMapTensorProduct ↑e₁
          (_ : ∀ (a₁ a₂ : Aᵐᵒᵖ) (b₁ b₂ : Bᵐᵒᵖ), ↑e₁ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₁ (a₁ ⊗ₜ[R] b₁) * ↑e₁ (a₂ ⊗ₜ[R] b₂))
          (_ : ↑e₁ (1 ⊗ₜ[R] 1) = 1))
        (AlgHom.opComm
          (algHomOfLinearMapTensorProduct ↑e₂
            (_ : ∀ (a₁ a₂ : A) (b₁ b₂ : B), ↑e₂ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₂ (a₁ ⊗ₜ[R] b₁) * ↑e₂ (a₂ ⊗ₜ[R] b₂))
            (_ : ↑e₂ (1 ⊗ₜ[R] 1) = 1)))) =
    AlgHom.op.symm (AlgHom.id S (A ⊗[R] B)ᵐᵒᵖ)

Next tactic:
ext
/-- `MulOpposite` distributes over `TensorProduct`. Note this is an `S`-algebra morphism, where
`A/S/R` is a tower of algebras. -/
def opAlgEquiv : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₐ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  letI e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr
      (opLinearEquiv S).symm (opLinearEquiv R).symm ≪≫ₗ opLinearEquiv S
  letI e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
  AlgEquiv.ofAlgHom
    (algHomOfLinearMapTensorProduct e₁.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.opComm <| algHomOfLinearMapTensorProduct e₂.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.op.symm.injective <| by ext <;> 

Proof state:
case ha.H
R : Type u_1
S : Type u_2
A : Type u_3
B : Type u_4
inst✝⁸ : CommSemiring R
inst✝⁷ : CommSemiring S
inst✝⁶ : Semiring A
inst✝⁵ : Semiring B
inst✝⁴ : Algebra R S
inst✝³ : Algebra R A
inst✝² : Algebra R B
inst✝¹ : Algebra S A
inst✝ : IsScalarTower R S A
e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (LinearEquiv.symm (opLinearEquiv S)) (LinearEquiv.symm (opLinearEquiv R)) ≪≫ₗ
    opLinearEquiv S
e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
x✝ : A
⊢ (AlgHom.comp
        (AlgHom.op.symm
          (AlgHom.comp
            (algHomOfLinearMapTensorProduct ↑e₁
              (_ :
                ∀ (a₁ a₂ : Aᵐᵒᵖ) (b₁ b₂ : Bᵐᵒᵖ),
                  ↑e₁ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₁ (a₁ ⊗ₜ[R] b₁) * ↑e₁ (a₂ ⊗ₜ[R] b₂))
              (_ : ↑e₁ (1 ⊗ₜ[R] 1) = 1))
            (AlgHom.opComm
              (algHomOfLinearMapTensorProduct ↑e₂
                (_ : ∀ (a₁ a₂ : A) (b₁ b₂ : B), ↑e₂ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₂ (a₁ ⊗ₜ[R] b₁) * ↑e₂ (a₂ ⊗ₜ[R] b₂))
                (_ : ↑e₂ (1 ⊗ₜ[R] 1) = 1)))))
        includeLeft)
      x✝ =
    (AlgHom.comp (AlgHom.op.symm (AlgHom.id S (A ⊗[R] B)ᵐᵒᵖ)) includeLeft) x✝

Next tactic:
rfl
/-- `MulOpposite` distributes over `TensorProduct`. Note this is an `S`-algebra morphism, where
`A/S/R` is a tower of algebras. -/
def opAlgEquiv : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₐ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  letI e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr
      (opLinearEquiv S).symm (opLinearEquiv R).symm ≪≫ₗ opLinearEquiv S
  letI e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
  AlgEquiv.ofAlgHom
    (algHomOfLinearMapTensorProduct e₁.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.opComm <| algHomOfLinearMapTensorProduct e₂.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.op.symm.injective <| by ext <;> 

Proof state:
case hb.H
R : Type u_1
S : Type u_2
A : Type u_3
B : Type u_4
inst✝⁸ : CommSemiring R
inst✝⁷ : CommSemiring S
inst✝⁶ : Semiring A
inst✝⁵ : Semiring B
inst✝⁴ : Algebra R S
inst✝³ : Algebra R A
inst✝² : Algebra R B
inst✝¹ : Algebra S A
inst✝ : IsScalarTower R S A
e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (LinearEquiv.symm (opLinearEquiv S)) (LinearEquiv.symm (opLinearEquiv R)) ≪≫ₗ
    opLinearEquiv S
e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
x✝ : B
⊢ (AlgHom.comp
        (AlgHom.restrictScalars R
          (AlgHom.op.symm
            (AlgHom.comp
              (algHomOfLinearMapTensorProduct ↑e₁
                (_ :
                  ∀ (a₁ a₂ : Aᵐᵒᵖ) (b₁ b₂ : Bᵐᵒᵖ),
                    ↑e₁ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₁ (a₁ ⊗ₜ[R] b₁) * ↑e₁ (a₂ ⊗ₜ[R] b₂))
                (_ : ↑e₁ (1 ⊗ₜ[R] 1) = 1))
              (AlgHom.opComm
                (algHomOfLinearMapTensorProduct ↑e₂
                  (_ :
                    ∀ (a₁ a₂ : A) (b₁ b₂ : B), ↑e₂ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₂ (a₁ ⊗ₜ[R] b₁) * ↑e₂ (a₂ ⊗ₜ[R] b₂))
                  (_ : ↑e₂ (1 ⊗ₜ[R] 1) = 1))))))
        includeRight)
      x✝ =
    (AlgHom.comp (AlgHom.restrictScalars R (AlgHom.op.symm (AlgHom.id S (A ⊗[R] B)ᵐᵒᵖ))) includeRight) x✝

Next tactic:
rfl
/-- `MulOpposite` distributes over `TensorProduct`. Note this is an `S`-algebra morphism, where
`A/S/R` is a tower of algebras. -/
def opAlgEquiv : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₐ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  letI e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr
      (opLinearEquiv S).symm (opLinearEquiv R).symm ≪≫ₗ opLinearEquiv S
  letI e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
  AlgEquiv.ofAlgHom
    (algHomOfLinearMapTensorProduct e₁.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.opComm <| algHomOfLinearMapTensorProduct e₂.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.op.symm.injective <| by ext <;> rfl) (by 

Proof state:
R : Type u_1
S : Type u_2
A : Type u_3
B : Type u_4
inst✝⁸ : CommSemiring R
inst✝⁷ : CommSemiring S
inst✝⁶ : Semiring A
inst✝⁵ : Semiring B
inst✝⁴ : Algebra R S
inst✝³ : Algebra R A
inst✝² : Algebra R B
inst✝¹ : Algebra S A
inst✝ : IsScalarTower R S A
e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (LinearEquiv.symm (opLinearEquiv S)) (LinearEquiv.symm (opLinearEquiv R)) ≪≫ₗ
    opLinearEquiv S
e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
⊢ AlgHom.comp
      (AlgHom.opComm
        (algHomOfLinearMapTensorProduct ↑e₂
          (_ : ∀ (a₁ a₂ : A) (b₁ b₂ : B), ↑e₂ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₂ (a₁ ⊗ₜ[R] b₁) * ↑e₂ (a₂ ⊗ₜ[R] b₂))
          (_ : ↑e₂ (1 ⊗ₜ[R] 1) = 1)))
      (algHomOfLinearMapTensorProduct ↑e₁
        (_ : ∀ (a₁ a₂ : Aᵐᵒᵖ) (b₁ b₂ : Bᵐᵒᵖ), ↑e₁ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₁ (a₁ ⊗ₜ[R] b₁) * ↑e₁ (a₂ ⊗ₜ[R] b₂))
        (_ : ↑e₁ (1 ⊗ₜ[R] 1) = 1)) =
    AlgHom.id S (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)

Next tactic:
ext
/-- `MulOpposite` distributes over `TensorProduct`. Note this is an `S`-algebra morphism, where
`A/S/R` is a tower of algebras. -/
def opAlgEquiv : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₐ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  letI e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr
      (opLinearEquiv S).symm (opLinearEquiv R).symm ≪≫ₗ opLinearEquiv S
  letI e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
    TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
  AlgEquiv.ofAlgHom
    (algHomOfLinearMapTensorProduct e₁.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.opComm <| algHomOfLinearMapTensorProduct e₂.toLinearMap
      (fun a₁ a₂ b₁ b₂ => unop_injective rfl) (unop_injective rfl))
    (AlgHom.op.symm.injective <| by ext <;> rfl) (by ext <;> 

Proof state:
case ha.H
R : Type u_1
S : Type u_2
A : Type u_3
B : Type u_4
inst✝⁸ : CommSemiring R
inst✝⁷ : CommSemiring S
inst✝⁶ : Semiring A
inst✝⁵ : Semiring B
inst✝⁴ : Algebra R S
inst✝³ : Algebra R A
inst✝² : Algebra R B
inst✝¹ : Algebra S A
inst✝ : IsScalarTower R S A
e₁ : Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ ≃ₗ[S] (A ⊗[R] B)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (LinearEquiv.symm (opLinearEquiv S)) (LinearEquiv.symm (opLinearEquiv R)) ≪≫ₗ
    opLinearEquiv S
e₂ : A ⊗[R] B ≃ₗ[S] (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)ᵐᵒᵖ :=
  TensorProduct.AlgebraTensorModule.congr (opLinearEquiv S) (opLinearEquiv R) ≪≫ₗ opLinearEquiv S
x✝ : Aᵐᵒᵖ
⊢ (AlgHom.comp
        (AlgHom.comp
          (AlgHom.opComm
            (algHomOfLinearMapTensorProduct ↑e₂
              (_ : ∀ (a₁ a₂ : A) (b₁ b₂ : B), ↑e₂ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₂ (a₁ ⊗ₜ[R] b₁) * ↑e₂ (a₂ ⊗ₜ[R] b₂))
              (_ : ↑e₂ (1 ⊗ₜ[R] 1) = 1)))
          (algHomOfLinearMapTensorProduct ↑e₁
            (_ :
              ∀ (a₁ a₂ : Aᵐᵒᵖ) (b₁ b₂ : Bᵐᵒᵖ), ↑e₁ ((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) = ↑e₁ (a₁ ⊗ₜ[R] b₁) * ↑e₁ (a₂ ⊗ₜ[R] b₂))
            (_ : ↑e₁ (1 ⊗ₜ[R] 1) = 1)))
        includeLeft)
      x✝ =
    (AlgHom.comp (AlgHom.id S (Aᵐᵒᵖ ⊗[R] Bᵐᵒᵖ)) includeLeft) x✝

Next tactic:
rfl