Theorem: Nat.totient_gcd_mul_totient_mul
File: Mathlib/Data/Nat/Totient.lean

tactic: have shuffle :
  ∀ a1 a2 b1 b2 c1 c2 : ℕ,
    b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2) := by
  intro a1 a2 b1 b2 c1 c2 h1 h2
  calc
    a1 / b1 * c1 * (a2 / b2 * c2) = a1 / b1 * (a2 / b2) * (c1 * c2) := by apply mul_mul_mul_comm
    _ = a1 * a2 / (b1 * b2) * (c1 * c2) := by
      congr 1
      exact div_mul_div_comm h1 h2
state_before: a b : ℕ
⊢ φ (gcd a b) * φ (a * b) = φ a * φ b * gcd a b

tactic: simp only [totient_eq_div_factors_mul]
state_before: a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ φ (gcd a b) * φ (a * b) = φ a * φ b * gcd a b

tactic: rw [shuffle, shuffle]
state_before: a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ((gcd a b / ∏ p in List.toFinset (factors (gcd a b)), p) * ∏ p in List.toFinset (factors (gcd a b)), (p - 1)) *
      ((a * b / ∏ p in List.toFinset (factors (a * b)), p) * ∏ p in List.toFinset (factors (a * b)), (p - 1)) =
    ((a / ∏ p in List.toFinset (factors a), p) * ∏ p in List.toFinset (factors a), (p - 1)) *
        ((b / ∏ p in List.toFinset (factors b), p) * ∏ p in List.toFinset (factors b), (p - 1)) *
      gcd a b

tactic: rotate_left
state_before: a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ gcd a b * (a * b) / ((∏ p in List.toFinset (factors (gcd a b)), p) * ∏ p in List.toFinset (factors (a * b)), p) *
      ((∏ p in List.toFinset (factors (gcd a b)), (p - 1)) * ∏ p in List.toFinset (factors (a * b)), (p - 1)) =
    a * b / ((∏ p in List.toFinset (factors a), p) * ∏ p in List.toFinset (factors b), p) *
        ((∏ p in List.toFinset (factors a), (p - 1)) * ∏ p in List.toFinset (factors b), (p - 1)) *
      gcd a b

case a
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ∏ p in List.toFinset (factors a), p ∣ a

case a
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ∏ p in List.toFinset (factors b), p ∣ b

case a
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ∏ p in List.toFinset (factors (gcd a b)), p ∣ gcd a b

case a
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ∏ p in List.toFinset (factors (a * b)), p ∣ a * b

tactic: repeat' apply prod_prime_factors_dvd
state_before: case a
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ∏ p in List.toFinset (factors a), p ∣ a

case a
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ∏ p in List.toFinset (factors b), p ∣ b

case a
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ∏ p in List.toFinset (factors (gcd a b)), p ∣ gcd a b

case a
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ ∏ p in List.toFinset (factors (a * b)), p ∣ a * b

a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ gcd a b * (a * b) / ((∏ p in List.toFinset (factors (gcd a b)), p) * ∏ p in List.toFinset (factors (a * b)), p) *
      ((∏ p in List.toFinset (factors (gcd a b)), (p - 1)) * ∏ p in List.toFinset (factors (a * b)), (p - 1)) =
    a * b / ((∏ p in List.toFinset (factors a), p) * ∏ p in List.toFinset (factors b), p) *
        ((∏ p in List.toFinset (factors a), (p - 1)) * ∏ p in List.toFinset (factors b), (p - 1)) *
      gcd a b

tactic: intro a1 a2 b1 b2 c1 c2 h1 h2
state_before: a b : ℕ
⊢ ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)

tactic: calc
  a1 / b1 * c1 * (a2 / b2 * c2) = a1 / b1 * (a2 / b2) * (c1 * c2) := by apply mul_mul_mul_comm
  _ = a1 * a2 / (b1 * b2) * (c1 * c2) := by
    congr 1
    exact div_mul_div_comm h1 h2
state_before: a b a1 a2 b1 b2 c1 c2 : ℕ
h1 : b1 ∣ a1
h2 : b2 ∣ a2
⊢ a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)

tactic: apply mul_mul_mul_comm
state_before: a b a1 a2 b1 b2 c1 c2 : ℕ
h1 : b1 ∣ a1
h2 : b2 ∣ a2
⊢ a1 / b1 * c1 * (a2 / b2 * c2) = a1 / b1 * (a2 / b2) * (c1 * c2)

tactic: congr 1
state_before: a b a1 a2 b1 b2 c1 c2 : ℕ
h1 : b1 ∣ a1
h2 : b2 ∣ a2
⊢ a1 / b1 * (a2 / b2) * (c1 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)

tactic: exact div_mul_div_comm h1 h2
state_before: case e_a
a b a1 a2 b1 b2 c1 c2 : ℕ
h1 : b1 ∣ a1
h2 : b2 ∣ a2
⊢ a1 / b1 * (a2 / b2) = a1 * a2 / (b1 * b2)

tactic: apply prod_prime_factors_dvd
state_before: a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ gcd a b * (a * b) / ((∏ p in List.toFinset (factors (gcd a b)), p) * ∏ p in List.toFinset (factors (a * b)), p) *
      ((∏ p in List.toFinset (factors (gcd a b)), (p - 1)) * ∏ p in List.toFinset (factors (a * b)), (p - 1)) =
    a * b / ((∏ p in List.toFinset (factors a), p) * ∏ p in List.toFinset (factors b), p) *
        ((∏ p in List.toFinset (factors a), (p - 1)) * ∏ p in List.toFinset (factors b), (p - 1)) *
      gcd a b

tactic: simp only [prod_factors_gcd_mul_prod_factors_mul]
state_before: a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ gcd a b * (a * b) / ((∏ p in List.toFinset (factors (gcd a b)), p) * ∏ p in List.toFinset (factors (a * b)), p) *
      ((∏ p in List.toFinset (factors (gcd a b)), (p - 1)) * ∏ p in List.toFinset (factors (a * b)), (p - 1)) =
    a * b / ((∏ p in List.toFinset (factors a), p) * ∏ p in List.toFinset (factors b), p) *
        ((∏ p in List.toFinset (factors a), (p - 1)) * ∏ p in List.toFinset (factors b), (p - 1)) *
      gcd a b

tactic: rw [eq_comm, mul_comm, ← mul_assoc, ← Nat.mul_div_assoc]
state_before: a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ gcd a b * (a * b) / ((∏ p in List.toFinset (factors a), p) * ∏ p in List.toFinset (factors b), p) *
      ((∏ p in List.toFinset (factors a), (p - 1)) * ∏ p in List.toFinset (factors b), (p - 1)) =
    a * b / ((∏ p in List.toFinset (factors a), p) * ∏ p in List.toFinset (factors b), p) *
        ((∏ p in List.toFinset (factors a), (p - 1)) * ∏ p in List.toFinset (factors b), (p - 1)) *
      gcd a b

tactic: exact mul_dvd_mul (prod_prime_factors_dvd a) (prod_prime_factors_dvd b)
state_before: case H
a b : ℕ
shuffle : ∀ (a1 a2 b1 b2 c1 c2 : ℕ), b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2)
⊢ (∏ p in List.toFinset (factors a), p) * ∏ p in List.toFinset (factors b), p ∣ a * b