import data.real.basic
import data.nat.choose.sum -- binomial theorem
import data.real.ereal
import data.pnat.basic

-- namespace problem_sheet_two
section Q2

/-!

# Q2

-/

/-
2.  Fix nonempty sets S_n ⊆ R, n= 1,2,3,....
Prove that sup{sup S1,sup S2,sup S3,...} = sup(⋃_{n=1}^{infty} S_n),
in the sense that if either exists then so does the other, and they are equal.
-/

/-
Let's first answer a better question with fewer restrictions,
using extended reals.
Then Sup {Sup (S_i) : i ∈ I} = Sup (⋃_{i ∈ I} S_i) is *always* true
-/

example (I : Type) (S : I → set (ereal)) :
  Sup (set.range (λ i, Sup (S i))) = Sup (⋃ i, S i) :=
begin
  apply le_antisymm,
  { rw Sup_le_iff,
    rintro - ⟨i, rfl⟩,
    dsimp only,
    refine Sup_le_Sup _,
    exact set.subset_Union S i },
  { rw Sup_le_iff,
    rintros x ⟨-, ⟨i, rfl⟩, (hix : x ∈ S i)⟩,
    apply le_trans (le_Sup hix),
    apply le_Sup _,
    use i }
end

lemma exists_lub (S : set ℝ) :
  S.nonempty ∧ (upper_bounds S).nonempty → (∃ B, is_lub S B) :=
begin
  rintros ⟨h1, h2⟩,
  cases real.exists_sup S h1 h2 with B hB,
  use B,
  split,
  { apply (hB B).1,
    refl },
  { intros x hx,
    rw hB,
    exact hx }
end

-- "All the S_i have a sup and the set {sup S1, sup S2, ...} has a sup, if and
-- only if the union of the S_i has a sup"
theorem Q2a (S : ℕ+ → set ℝ)
  (hS : ∀ i : ℕ+, (S i).nonempty) :
  (∀ n : ℕ+, ∃ B, is_lub (S n) B) ∧ (∃ B, is_lub (set.range (λ i, Sup (S i))) B) ↔
  ∃ B, is_lub (⋃ i, S i) B :=
begin
  split,
  { rintro ⟨h1, B, hB1, hB2⟩,
    apply exists_lub,
    cases hS 37 with x hx,
    use [x, S 37, 37, hx],
    use B,
    rintros y ⟨-, ⟨i, rfl⟩, (hiY : y ∈ S i)⟩,
    specialize hB1 ⟨i, rfl⟩,
    refine le_trans _ hB1,
    apply real.le_Sup _ _ hiY,
    rcases h1 i with ⟨C, hC1, hC2⟩,
    use C,
    exact hC1 },
  { rintro ⟨B, hB1, hB2⟩,
    split,
    { intro n,
      apply exists_lub,
      use hS n,
      use B,
      apply upper_bounds_mono_set _ hB1,
      exact set.subset_Union S n },
    { apply exists_lub,
      split,
      { apply set.range_nonempty },
      { use B,
        rintro - ⟨i, rfl⟩,
        dsimp only,
        rw real.Sup_le _ (hS i),
        { intros z hz,
          apply hB1,
          use [S i, i, hz] },
        { use B,
          intros z hz,
          apply hB1,
          use [S i, i, hz] } } } }
end

-- assuming both sides make sense, prove the sups are equal
theorem Q2b (S : ℕ+ → set ℝ)
  (hS : ∀ i : ℕ+, (S i).nonempty)
  (hLHS: ∀ n : ℕ+, ∃ B, is_lub (S n) B) (hLHS' : ∃ B, is_lub (set.range (λ i, Sup (S i))) B)
  (hRHS : ∃ B, is_lub (⋃ i, S i) B) :
  Sup (set.range (λ i, Sup (S i))) = Sup (⋃ i, S i) :=
begin
  -- suffices to prove LHS ≤ RHS and RHS ≤ LHS
  apply le_antisymm,
  { -- to prove Sup ≤ something, suffices to prove all elements are ≤ it
    rw real.Sup_le,
    rintro - ⟨j, rfl⟩,
    dsimp only,
    -- so it suffices to prove Sup (S j) ≤ Sup (⋃_i S i)
    rw real.Sup_le,
    -- so it even suffices to prove every element of S j is ≤ the Sup
    intros x hx,
    -- but this is clear because the elements are in the set
    apply real.le_Sup, swap,
    use [S j, j, hx],
    -- now just check that all sets were nonempty and had least upper bounds,
    -- this follows from our assumptions.
    { rcases hRHS with ⟨B, hB1, hB2⟩,
      use B,
      exact hB1 },
    { exact hS j},
    { rcases hLHS j with ⟨B, hB1, hB2⟩,
      use B,
      exact hB1 },
    { apply set.range_nonempty },
    { rcases hLHS' with ⟨B, hB1, hB2⟩,
      use B,
      exact hB1 } },
  { -- to prove Sup ≤ somthing, suffices to prove all elements are ≤ it
    rw real.Sup_le,
    -- so assume z is in one of the S i's, call it S j
    rintro z ⟨-, ⟨j, rfl⟩, (hzj : z ∈ S j)⟩,
    -- then z ≤ Sup (S j)
    apply le_trans (real.le_Sup _ _ hzj),
    -- so it's certainly ≤ Sup of a set containing Sup (S j)
    apply real.le_Sup,
    -- now tidy up
    { rcases hLHS' with ⟨B, hB1, hB2⟩,
      use B,
      exact hB1 },
    { use j },
    { rcases hLHS j with ⟨B, hB1, hB2⟩,
      use B,
      exact hB1 },
    { cases hS 37 with x hx,
      use [x, S 37, 37, hx],
    },
    { rcases hRHS with ⟨B, hB1, hB2⟩,
      use B,
      exact hB1 } }
end

end Q2

/-!

# Q3

Take bounded, nonempty `S, T ⊆ ℝ`.
Define `S + T := { s + t : s ∈ S, t ∈ T}`.
Prove `sup(S+T)  =  sup(S)+ sup(T)`

-/

theorem is_lub_def {S : set ℝ} {a : ℝ} :
  is_lub S a ↔ a ∈ upper_bounds S ∧ ∀ x, x ∈ upper_bounds S → a ≤ x :=
begin
  refl
end

-- #check mem_upper_bounds -- a ∈ upper_bounds S ↔ ∀ x, x ∈ S → x ≤ a

--example (P Q : Prop) : P → Q ↔ ¬ Q → ¬ P :=
--by library_search

theorem useful_lemma {S : set ℝ} {a : ℝ} (haS : is_lub S a) (t : ℝ)
  (ht : t < a) : ∃ s, s ∈ S ∧ t < s :=
begin
  rw is_lub_def at haS,
  cases haS with haS1 haS2,
  specialize haS2 t,
  replace haS2 := mt haS2,
  push_neg at haS2,
  specialize haS2 ht,
  rw mem_upper_bounds at haS2,
  push_neg at haS2,
  exact haS2,
end

theorem problem_sheet_two.Q3 (S T : set ℝ) (a b : ℝ) :
  is_lub S a → is_lub T b → is_lub (S + T) (a + b) :=
begin
  intros hSa hTb,
  rw is_lub_def,
  split,
  { rw mem_upper_bounds,
    intro x,
    intro hx,
    rcases hx with ⟨s, t, hsS, htT, rfl⟩,
    rw is_lub_def at hSa hTb,
    rcases hSa with ⟨ha1, ha2⟩,
    rcases hTb with ⟨hb1, hb2⟩,
    rw mem_upper_bounds at ha1 hb1,
    specialize ha1 s hsS,
    specialize hb1 t htT,
    linarith,
  },
  { intro x,
    intro hx,
    rw mem_upper_bounds at hx,
    by_contra,
    push_neg at h,
    set ε := a + b - x with hε,
    have hε2 : 0 < ε,
      linarith,
    set a' := a - ε/2 with ha',
    set b' := b - ε/2 with hb',
    rcases useful_lemma hSa a' (by linarith) with ⟨s', hs', hs'2⟩,
    rcases useful_lemma hTb b' (by linarith) with ⟨t', ht', ht'2⟩,
    specialize hx (s' + t') ⟨s', t', hs', ht', rfl⟩,
    linarith,
  }
end

/-!

# Q4

Fix `a ∈ (0,∞)` and `n : ℕ`. We will prove
`∃ x : ℝ, x^n = a`.

-/

section Q4

noncomputable theory

section one

-- this first section, a and n are going to be variables
variables {a : ℝ} (ha : 0 < a) {n : ℕ} (hn : 0 < n)

include ha hn

/- Note: We do things in a slightly different order to the question
on the sheet, because the way it's set up on the sheet, `x` is
defined to be `Sup(S_a)` very early on, which technically forces
us to prove that `1/x` is `Sup(S_{1/a})` -- this is unnecessary
with the approach below. I broke this question into five "parts".
-/

/-
2) For `ε ∈ (0,1)` and arbitrary `x ≥ 0` show `(x+ε)ⁿ ≤ x^n + ε[(x + 1)ⁿ − xⁿ].`
(Hint: multiply out.)
-/

theorem part2 {x : ℝ} (x_nonneg : 0 ≤ x) (ε : ℝ) (hε0 : 0 < ε) (hε1 : ε < 1) :
  (x + ε)^n ≤ x^n + ε * ((x + 1)^n - x^n) :=
begin
  rw [ add_pow, add_pow ],
  simp_rw [one_pow, mul_one],
  rw [finset.sum_range_succ, nat.sub_self, pow_zero, mul_one],
  rw [nat.choose_self, nat.cast_one, mul_one],
  apply add_le_add_left,
  rw [finset.sum_range_succ, nat.choose_self, nat.cast_one, mul_one],
  rw [add_sub_cancel', finset.mul_sum],
  apply finset.sum_le_sum,
  intros i hi,
  rw [finset.mem_range, nat.lt_iff_add_one_le, le_iff_exists_add] at hi,
  -- rcases hi with ⟨c, rfl⟩, -- rcases bug?
  cases hi with c hc,
  have hni : n - i = c + 1,
    omega,
  rw [hni, mul_comm (x ^ i), mul_assoc, pow_succ, mul_comm ε, mul_assoc],
  apply mul_le_of_le_one_left,
  { apply mul_nonneg (le_of_lt hε0),
    apply mul_nonneg (pow_nonneg x_nonneg i),
    norm_cast, simp },
  { apply pow_le_one; linarith }
end

/-
3) Hence show that if `x ≥ 0` and `xⁿ < a` then
`∃ ε ∈ (0,1)` such that `(x+ε)ⁿ < a.` (*)
-/

theorem part3 {x : ℝ} (x_nonneg : 0 ≤ x) (h : x ^ n < a) :
  ∃ ε : ℝ, 0 < ε ∧ ε < 1 ∧ (x+ε)^n < a :=
begin
  set ε := min (1 / 2) ((a - x ^ n) / ((x + 1) ^ n - x ^ n) / 2) with hε,
  use ε,
  have hx1 : 0 < (x + 1) ^ n - x ^ n,
    rw sub_pos,
    apply pow_lt_pow_of_lt_left (lt_add_one _) x_nonneg hn,
  have hε0 : 0 < ε,
    rw [hε, lt_min_iff],
    split, { linarith },
    refine div_pos _ (by linarith),
    refine div_pos (by linarith) hx1,
  use hε0,
  have hε1 : ε < 1,
    suffices : ε ≤ 1/2, linarith,
    rw hε,
    exact min_le_left _ _,
  use hε1,
  apply lt_of_le_of_lt (part2 ha hn x_nonneg ε hε0 hε1),
  rw ← lt_sub_iff_add_lt',
  rw ← lt_div_iff hx1,
  apply lt_of_le_of_lt (min_le_right _ _),
  apply div_two_lt_of_pos,
  exact div_pos (by linarith) hx1,
end

/-
4) If `x > 0` is arbitrary and `xⁿ > a`, deduce from (∗) (i.e. part 3) that
`∃ ε ∈ (0,1)` such that `(1/x+ε)ⁿ < 1/a`. (∗∗)

Note that here it makes life much easier to have x a variable and not
the fixed Sup(S_a); with the approach on the sheet one would have to
technical prove that 1/x is Sup(S_{1/a}), which is not necessary
with this approach.
-/

theorem part4 {x : ℝ} (hx : 0 < x) (h : a < x^n) :
  ∃ ε : ℝ, 0 < ε ∧ ε < 1 ∧ (1/x + ε)^n < 1/a :=
begin
  have h1x_pos : 0 < 1/x,
    rwa one_div_pos,
  have h1xa : (1/x)^n < 1/a,
    rw [div_pow, one_pow],
    apply div_lt_div_of_lt_left ha h zero_lt_one,
  exact part3 (one_div_pos.2 ha) hn (le_of_lt h1x_pos) h1xa,
end

end one

section two

-- in this section, a and n are going to be fixed parameters

parameters {a : ℝ} (ha : 0 < a) {n : ℕ} (hn : 0 < n)

include ha hn
/-
1) Set `Sₐ := {s ∈ [0,∞) : s^n < a}` and show `Sₐ` is nonempty and
bounded above, so we may define `x := sup Sₐ`
-/

def S := {s : ℝ | 0 ≤ s ∧ s ^ n < a}

theorem part1 : (∃ s : ℝ, s ∈ S) ∧ (∃ B : ℝ, ∀ s ∈ S, s ≤ B ) :=
begin
  split,
  { use 0,
    split, { refl },
    convert ha,
    simp [hn] },
  { use a + 1,
    rintro s ⟨hs1, hs2⟩,
    by_contra hsa,
    push_neg at hsa,
    have hs3 : 1 ≤ s,
      linarith,
    have hs4 : s ≤ s ^ n,
      cases n, cases hn,
      suffices : 1 ≤ s ^ n,
        convert mul_le_mul (le_refl s) this (by linarith) hs1,
        { simp },
      exact one_le_pow_of_one_le hs3 _,
    linarith, }
end

def x := Sup S

theorem is_lub_x : is_lub S x :=
begin
  cases part1 with nonempty bdd,
  cases nonempty with x hx,
  cases bdd with y hy,
  exact real.is_lub_Sup hx hy,
end

lemma x_nonneg : 0 ≤ x :=
begin
  rcases is_lub_x with ⟨h, -⟩,
  apply h,
  split, refl,
  convert ha,
  simp [hn],
end

lemma easy (h : a < x^n) : x ≠ 0 :=
begin
  intro hx,
  rw hx at h,
  suffices : a < 0,
    linarith,
  convert h,
  symmetry, -- todo add simp lemma to mathlib
  simp [hn],
end

/-
5) Deduce contradictions from (∗) and (∗∗) to show that `xⁿ = a`.
-/

-- lemma le_of_pow_le_pow (n : ℕ) (hn : 0 < n) (x y : ℝ) (h : x^n ≤ y^n) : x ≤ y :=
-- begin
--   by_contra hxy,
--   push_neg at hxy,
--   sorry,
-- --  have h2 : pow_lt_pow
-- end

theorem part5 : x^n = a :=
begin
  rcases lt_trichotomy (x^n) a with (hlt | heq | hgt),
  { exfalso,
    obtain ⟨ε, hε0, -, hε⟩ := part3 ha hn (x_nonneg) hlt,
    have ZZZ := is_lub_x.1 ⟨_, hε⟩, linarith,
    linarith [x_nonneg] },
  { assumption },
  { exfalso,
    have hxpow : 0 < x,
      rw lt_iff_le_and_ne,
      exact ⟨x_nonneg, (easy hgt).symm⟩,
    obtain ⟨ε, hε0, -, hε⟩ := part4 ha hn hxpow hgt,
    have huseful : 0 < 1 / x + ε,
      refine add_pos _ hε0,
      exact one_div_pos.mpr hxpow,
    have ha' : a < (1 / (1 / x + ε)) ^ n,
    { rw [div_pow, one_pow],
      rwa lt_one_div _ ha at hε,
      apply pow_pos,
      assumption },
    have hproblem : (1 / (1 / x + ε)) ∈ upper_bounds S,
    { rintro t ⟨ht0, hta⟩,
      apply le_of_lt,
      apply lt_of_pow_lt_pow n,
      { apply le_of_lt,
        rwa one_div_pos },
      { linarith } },
    have ZZZ := is_lub_x.2 hproblem,
      rw le_one_div at ZZZ,
      { linarith },
      { assumption },
      { assumption } }
end

end two

end Q4

section Q6

/-!

# Q6

"Do (a) from first principles and then do parts (b)-(g) from (a)"

-/

-- We introduce the usual mathematical notation for absolute value
local notation `|` x `|` := abs x

/-
Useful for this one: `unfold`, `split_ifs` if you want to prove
from first principles, or guessing the name of the library function
if you want to use the library.
-/
theorem Q6a (x y : ℝ) : | x + y | ≤ | x | + | y | :=
begin
  unfold abs max,
  split_ifs;
  linarith
end

-- all the rest you're supposed to do using Q6a somehow:
-- `simp` and `linarith` are useful.

theorem Q6b (x y : ℝ) : |x + y| ≥ |x| - |y| :=
begin
  have h := Q6a (x + y) (-y),
  simp at h,
  linarith,
end

theorem Q6c (x y : ℝ) : |x + y| ≥ |y| - |x| :=
begin
  have h := Q6a (x + y) (-x),
  simp at h,
  linarith,
end

theorem Q6d (x y : ℝ) : |x - y| ≥ | |x| - |y| | :=
begin
  show _ ≤ _,
  rw abs_le,
  split,
  { -- |y|<=|x|+|y-x|
    have h := Q6a x (y-x),
    simp at h,
    rw abs_sub,
    linarith },
  { have h := Q6a (x-y) y,
    simp at h,
    linarith }
end

theorem Q6e (x y : ℝ) : |x| ≤ |y| + |x - y| :=
begin
  have h := Q6a y (x-y),
  convert h,
  ring,
end

theorem Q6f (x y : ℝ) : |x| ≥ |y| - |x - y| :=
begin
  have h := Q6a x (y-x),
  simp at h,
  rw abs_sub at h,
  linarith,
end

theorem Q6g (x y z : ℝ) : |x - y| ≤ |x - z| + |y - z| :=
begin
  have h := Q6a (x-z) (z-y),
  rw abs_sub z y at h,
  convert h,
  ring,
end

end Q6


import data.nat.basic
import data.nat.gcd.basic
import data.nat.pow

import algebra.associated
import algebra.group_power.basic

/-
Iranian Mathematical Olympiad 1998, Problem 6

Let x,a,b be positive integers such that x^(a+b) = a^b * b. Prove
that a = x and b = x^x.
-/

theorem iran1998_q6
  (x a b : ℕ)
  (hx : 0 < x)
  (ha : 0 < a)
  (hb : 0 < b)
  (h : x^(a+b) = a^b * b)
 : a = x ∧ b = x^x :=
begin
  cases x,
  { exact false.elim (nat.lt_asymm hx hx) },
  cases x,
  { rw [one_pow] at h,
    have hab : 1 ≤ a ^ b := nat.one_le_pow b a ha,
    have habb : a ^ b ≤ a ^ b * b := nat.le_mul_of_pos_right hb,
    rw [←h] at habb,
    have hab' : a ^ b = 1 := le_antisymm habb hab,
    rw [hab', one_mul, eq_comm] at h,
    rw[one_pow],
    rw [h, pow_one] at hab',
    exact ⟨hab', h⟩ },
  let x' := x.succ.succ,
  have : ∀ p : ℕ, prime p → p ∣ x → p ∣ b,
  { intros p hpp hpx,
    sorry },
  sorry
end


import data.real.basic

--OUTPUT 1
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  -- $\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from by {
    -- $\leadstoandfrom$
    have h2 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / 2), from by {
      -- $\leadstoandfrom$
      have h3 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / (2 * (b + c + a))), from by {
        -- $\leadstoandfrom$
        have h4 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
          -- $\leadstoandfrom$
          have h5 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
            -- $\leadstoandfrom$
            have h6 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
              -- $\leadstoandfrom$
              have h7 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                -- $\leadstoandfrom$
                have h8 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                  -- $\leadstoandfrom$
                  have h9 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                    -- $\leadstoandfrom$
                    have h10 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                      -- $\leadstoandfrom$
                      have h11 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                        -- $\leadstoandfrom$
                        have h12 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                          -- $\leadstoandfrom$
                          have h13 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                            -- $\leadstoandfrom$
                            have h14 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                              -- $\leadstoandfrom$
                              have h15 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                -- $\leadstoandfrom$
                                have h16 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                  -- $\leadstoandfrom$
                                  have h17 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                    -- $\leadstoandfrom$
                                    have h18 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                      -- $\leadstoandfrom$
                                      have h19 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                        -- $\leadstoandfrom$
                                        have h20 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                          -- $\leadstoandfrom$
                                          have h21 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                            -- $\leadstoandfrom$
                                            have h22 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                              -- $\leadstoandfrom$
                                              have h23 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                                -- $\leadstoandfrom$
                                                have h24 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                                  -- $\leadstoandfrom$
                                                  have h25 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                                    -- $\leadstoandfrom$
                                                    have h26 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                                      -- $\leadstoandfrom$
                                                      have h27 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                                        -- $\leadstoandfrom$
                                                        have h28 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                                          -- $\leadstoandfrom$
                                                          have h29 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from by {
                                                            -- $\leadsto
end --Needs more than 2000 tokens!

/- FEW SHOT PROMPTS TO CODEX(START)
/--`theorem`
Power Set is Closed under Intersection
Let $S$ be a set.

Let $\powerset S$ be the power set of $S$.

Then:
:$\forall A, B \in \powerset S: A \cap B \in \powerset S$
`proof`
Let $A, B \in \powerset S$.

Then by the definition of power set, $A \subseteq S$ and $B \subseteq S$.

From Intersection is Subset we have that $A \cap B \subseteq A$.

It follows from Subset Relation is Transitive that $A \cap B \subseteq S$.

Thus $A \cap B \in \powerset S$ and closure is proved.
{{qed}}
-/
theorem power_set_intersection_closed {α : Type*} (S : set α) : ∀ A B ∈ 𝒫 S, (A ∩ B) ∈ 𝒫 S :=
begin
  -- $A$ and $B$ are sets. $A$ and $B$ belong to power set of $S$
  assume (A : set α) (hA : A ∈ 𝒫 S) (B : set α) (hB : B ∈ 𝒫 S),
  -- Then $A ⊆ S$ and $B ⊆ S$, by power set definition
  have h1 : (A ⊆ S) ∧ (B ⊆ S), from by {split,apply set.subset_of_mem_powerset,exact hA,apply set.subset_of_mem_powerset,exact hB},
  -- Then $(A ∩ B) ⊆ A$, by intersection of set is a subset
  have h2 : (A ∩ B) ⊆ A, from by apply set.inter_subset_left,
  -- Then $(A ∩ B) ⊆ S$, by subset relation is transitive
  have h3 : (A ∩ B) ⊆ S, from by {apply set.subset.trans h2 h1.left},
  -- Hence $(A ∩ B) ∈  𝒫 S$, by power set definition
  show (A ∩ B) ∈  𝒫 S, from by {apply set.mem_powerset h3},
end

/--`theorem`
Square of Sum
 :$\forall x, y \in \R: \paren {x + y}^2 = x^2 + 2 x y + y^2$
`proof`
Follows from the distribution of multiplication over addition:

{{begin-eqn}}
{{eqn | l = \left({x + y}\right)^2
      | r = \left({x + y}\right) \cdot \left({x + y}\right)
}}
{{eqn | r = x \cdot \left({x + y}\right) + y \cdot \left({x + y}\right)
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x \cdot x + x \cdot y + y \cdot x + y \cdot y
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x^2 + 2xy + y^2
      | c =
}}
{{end-eqn}}
{{qed}}
-/
theorem square_of_sum (x y : ℝ) : (x + y)^2 = (x^2 + 2*x*y + y^2) :=
begin
  -- expand the power
  calc (x + y)^2 = (x+y)*(x+y) : by rw sq
  -- distributive property of multiplication over addition gives:
  ... = x*(x+y) + y*(x+y) : by rw add_mul
  -- applying the above property further gives:
  ... = x*x + x*y + y*x + y*y : by {rw [mul_comm x (x+y),mul_comm y (x+y)], rw [add_mul,add_mul], ring}
  -- rearranging the terms using commutativity and adding gives:
  ... = x^2 + 2*x*y + y^2 : by {repeat {rw ← sq}, rw mul_comm y x, ring}
end

/--`theorem`
Identity of Group is Unique
Let $\struct {G, \circ}$ be a group. Then there is a unique identity element $e \in G$.
`proof`
From Group has Latin Square Property, there exists a unique $x \in G$ such that:
:$a x = b$

and there exists a unique $y \in G$ such that:
:$y a = b$

Setting $b = a$, this becomes:

There exists a unique $x \in G$ such that:
:$a x = a$

and there exists a unique $y \in G$ such that:
:$y a = a$

These $x$ and $y$ are both $e$, by definition of identity element.
{{qed}}
-/
theorem group_identity_unique {G : Type*} [group G] : ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a :=
begin
  -- Group has Latin Square Property
  have h1 : ∀ a b : G, ∃! x : G, a * x = b, from by {
    assume a b : G, use a⁻¹ * b, obviously, },
  have h2 : ∀ a b : G, ∃! y : G, y * a = b, from by {
    assume a b : G, use b * a⁻¹, obviously, },

  -- Setting $b = a$, this becomes:
  have h3 : ∀ a : G, ∃! x : G, a * x = a, from
    assume a : G, h1 a a,
  have h4 : ∀ a : G, ∃! y : G, y * a = a, from
    assume a : G, h2 a a,

  -- These $x$ and $y$ are both $(1 : G)$, by definition of identity element
  have h5 : ∀ a : G, classical.some (h3 a).exists = (1 : G), from assume a :G,
    exists_unique.unique (h3 a) (classical.some_spec (exists_unique.exists (h3 a)))
    (mul_one a),
  have h6 : ∀ a : G, classical.some (h4 a).exists = (1 : G), from assume a : G,
    exists_unique.unique (h4 a) (classical.some_spec (exists_unique.exists (h4 a))) (one_mul a),

  show ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a, from by {
    use (1 : G),
    have h7 : ∀ e : G, (∀ a : G, e * a = a ∧ a * e = a) → e = 1, from by {
      assume (e : G) (hident : ∀ a : G, e * a = a ∧ a * e = a),
      have h8 : ∀ a : G, e = classical.some (h3 a).exists, from assume (a : G),
        exists_unique.unique (h3 a) (hident a).right
        (classical.some_spec (exists_unique.exists (h3 a))),
      have h9 : ∀ a : G, e = classical.some (h4 a).exists, from assume (a : G),
        exists_unique.unique (h4 a) (hident a).left
        (classical.some_spec (exists_unique.exists (h4 a))),
      show e = (1 : G), from eq.trans (h9 e) (h6 _),
    },
    exact ⟨by obviously, h7⟩,
  }
end

/--`theorem`
Nesbitt's inequality
Let $a$, $b$ and $c$ be positive real numbers.

Then:
:$\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$

`proof`
{{begin-eqn}}
{{eqn | l = \frac a {b + c} + \frac b {a + c} + \frac c {a + b}
      | o = \ge
      | r = \dfrac 3 2
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {a + b + c} {b + c} + \frac {a + b + c} {a + c} + \frac {a + b + c} {a + b}
      | o = \ge
      | r = \frac 9 2
      | c = by adding $3$
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {a + b + c} {b + c} + \frac {a + b + c} {a + c} + \frac {a + b + c} {a + b}
      | o = \ge
      | r = \frac {9 \paren {a + b + c} } {\paren {b + c} + \paren {a + c} + \paren {a + b} }
      | c = as $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {\frac 1 {b + c} + \frac 1 {a + c} + \frac 1 {a + b} } 3
      | o = \ge
      | r = \frac 3 {\paren {b + c} + \paren {a + c} + \paren {a + b} }
      | c = dividing by $3 \paren {a + b + c}$
}}
{{end-eqn}}
These are the arithmetic mean and the harmonic mean of $\dfrac 1 {b + c}$, $\dfrac 1 {a + c}$ and $\dfrac 1 {a + b}$.

From Arithmetic Mean is Never Less than Harmonic Mean the last inequality is true.

Thus Nesbitt's Inequality holds.
{{qed}}

-/
theorem  nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
FEW SHOT PROMPTS TO CODEX(END)-/


import group_theory.quotient_group bundled_subgroup

namespace quotient_group

variables {G : Type*} [group G] {N : subgroup G} (h : is_normal_subgroup N)

open function

def quotient' {G : Type*} [group G] {N : subgroup G} (h : is_normal_subgroup N) := quotient N.carrier

instance quotient'_is_group {G : Type*} [group G] {N : subgroup G} (h : is_normal_subgroup N) :
group (quotient' h) := by unfold quotient'; letI : normal_subgroup (N.carrier) := h; apply_instance

def mk' {G : Type*} [group G] {N : subgroup G} (h : is_normal_subgroup N) : G →* quotient' h :=
by letI : normal_subgroup (N.carrier) := h; exact group_hom.mk (quotient_group.mk)

theorem mk'.surjective : surjective (mk' h) := begin
  intro q,
  use quotient.out' q,
  apply quotient.out_eq'
end

end quotient_group


/-
Copyright (c) 2020 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa

! This file was ported from Lean 3 source module data.polynomial.denoms_clearable
! leanprover-community/mathlib commit 97eab48559068f3d6313da387714ef25768fb730
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Polynomial.EraseLead
import Mathbin.Data.Polynomial.Eval

/-!
# Denominators of evaluation of polynomials at ratios

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

Let `i : R → K` be a homomorphism of semirings.  Assume that `K` is commutative.  If `a` and
`b` are elements of `R` such that `i b ∈ K` is invertible, then for any polynomial
`f ∈ R[X]` the "mathematical" expression `b ^ f.nat_degree * f (a / b) ∈ K` is in
the image of the homomorphism `i`.
-/

open Polynomial Finset

open Polynomial

section DenomsClearable

variable {R K : Type _} [Semiring R] [CommSemiring K] {i : R →+* K}

variable {a b : R} {bi : K}

#print DenomsClearable /-
-- TODO: use hypothesis (ub : is_unit (i b)) to work with localizations.
/-- `denoms_clearable` formalizes the property that `b ^ N * f (a / b)`
does not have denominators, if the inequality `f.nat_degree ≤ N` holds.

The definition asserts the existence of an element `D` of `R` and an
element `bi = 1 / i b` of `K` such that clearing the denominators of
the fraction equals `i D`.
-/
def DenomsClearable (a b : R) (N : ℕ) (f : R[X]) (i : R →+* K) : Prop :=
  ∃ (D : R)(bi : K), bi * i b = 1 ∧ i D = i b ^ N * eval (i a * bi) (f.map i)
#align denoms_clearable DenomsClearable
-/

/- warning: denoms_clearable_zero -> denomsClearable_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {K : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CommSemiring.{u2} K] {i : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))} {b : R} {bi : K} (N : Nat) (a : R), (Eq.{succ u2} K (HMul.hMul.{u2, u2, u2} K K K (instHMul.{u2} K (Distrib.toHasMul.{u2} K (NonUnitalNonAssocSemiring.toDistrib.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))))) bi (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) (fun (_x : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) => R -> K) (RingHom.hasCoeToFun.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) i b)) (OfNat.ofNat.{u2} K 1 (OfNat.mk.{u2} K 1 (One.one.{u2} K (AddMonoidWithOne.toOne.{u2} K (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} K (NonAssocSemiring.toAddCommMonoidWithOne.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))))))) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N (OfNat.ofNat.{u1} (Polynomial.{u1} R _inst_1) 0 (OfNat.mk.{u1} (Polynomial.{u1} R _inst_1) 0 (Zero.zero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.zero.{u1} R _inst_1)))) i)
but is expected to have type
  forall {R : Type.{u1}} {K : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CommSemiring.{u2} K] {i : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))} {b : R} {bi : K} (N : Nat) (a : R), (Eq.{succ u2} K (HMul.hMul.{u2, u2, u2} K ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => K) b) K (instHMul.{u2} K (NonUnitalNonAssocSemiring.toMul.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))) bi (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => K) _x) (MulHomClass.toFunLike.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))) (NonUnitalRingHomClass.toMulHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) (RingHomClass.toNonUnitalRingHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)) (RingHom.instRingHomClassRingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))))) i b)) (OfNat.ofNat.{u2} K 1 (One.toOfNat1.{u2} K (Semiring.toOne.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N (OfNat.ofNat.{u1} (Polynomial.{u1} R _inst_1) 0 (Zero.toOfNat0.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.zero.{u1} R _inst_1))) i)
Case conversion may be inaccurate. Consider using '#align denoms_clearable_zero denomsClearable_zeroₓ'. -/
theorem denomsClearable_zero (N : ℕ) (a : R) (bu : bi * i b = 1) : DenomsClearable a b N 0 i :=
  ⟨0, bi, bu, by
    simp only [eval_zero, RingHom.map_zero, MulZeroClass.mul_zero, Polynomial.map_zero]⟩
#align denoms_clearable_zero denomsClearable_zero

/- warning: denoms_clearable_C_mul_X_pow -> denomsClearable_C_mul_X_pow is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {K : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CommSemiring.{u2} K] {i : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))} {b : R} {bi : K} {N : Nat} (a : R), (Eq.{succ u2} K (HMul.hMul.{u2, u2, u2} K K K (instHMul.{u2} K (Distrib.toHasMul.{u2} K (NonUnitalNonAssocSemiring.toDistrib.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))))) bi (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) (fun (_x : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) => R -> K) (RingHom.hasCoeToFun.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) i b)) (OfNat.ofNat.{u2} K 1 (OfNat.mk.{u2} K 1 (One.one.{u2} K (AddMonoidWithOne.toOne.{u2} K (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} K (NonAssocSemiring.toAddCommMonoidWithOne.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))))))) -> (forall {n : Nat} (r : R), (LE.le.{0} Nat Nat.hasLe n N) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) => R -> (Polynomial.{u1} R _inst_1)) (RingHom.hasCoeToFun.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (Polynomial.C.{u1} R _inst_1) r) (HPow.hPow.{u1, 0, u1} (Polynomial.{u1} R _inst_1) Nat (Polynomial.{u1} R _inst_1) (instHPow.{u1, 0} (Polynomial.{u1} R _inst_1) Nat (Monoid.Pow.{u1} (Polynomial.{u1} R _inst_1) (MonoidWithZero.toMonoid.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toMonoidWithZero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))))) (Polynomial.X.{u1} R _inst_1) n)) i))
but is expected to have type
  forall {R : Type.{u1}} {K : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CommSemiring.{u2} K] {i : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))} {b : R} {bi : K} {N : Nat} (a : R), (Eq.{succ u2} K (HMul.hMul.{u2, u2, u2} K ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => K) b) K (instHMul.{u2} K (NonUnitalNonAssocSemiring.toMul.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))) bi (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => K) _x) (MulHomClass.toFunLike.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))) (NonUnitalRingHomClass.toMulHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) (RingHomClass.toNonUnitalRingHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)) (RingHom.instRingHomClassRingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))))) i b)) (OfNat.ofNat.{u2} K 1 (One.toOfNat1.{u2} K (Semiring.toOne.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))) -> (forall {n : Nat} (r : R), (LE.le.{0} Nat instLENat n N) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N (HMul.hMul.{u1, u1, u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (Polynomial.mul'.{u1} R _inst_1)) (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))))) (Polynomial.C.{u1} R _inst_1) r) (HPow.hPow.{u1, 0, u1} (Polynomial.{u1} R _inst_1) Nat (Polynomial.{u1} R _inst_1) (instHPow.{u1, 0} (Polynomial.{u1} R _inst_1) Nat (Monoid.Pow.{u1} (Polynomial.{u1} R _inst_1) (MonoidWithZero.toMonoid.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toMonoidWithZero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))))) (Polynomial.X.{u1} R _inst_1) n)) i))
Case conversion may be inaccurate. Consider using '#align denoms_clearable_C_mul_X_pow denomsClearable_C_mul_X_powₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Lean3.lean:132:4: warning: unsupported: rw with cfg: { occs := occurrences.pos[occurrences.pos] «expr[ ,]»([2]) } -/
theorem denomsClearable_C_mul_X_pow {N : ℕ} (a : R) (bu : bi * i b = 1) {n : ℕ} (r : R)
    (nN : n ≤ N) : DenomsClearable a b N (C r * X ^ n) i :=
  by
  refine' ⟨r * a ^ n * b ^ (N - n), bi, bu, _⟩
  rw [C_mul_X_pow_eq_monomial, map_monomial, ← C_mul_X_pow_eq_monomial, eval_mul, eval_pow, eval_C]
  rw [RingHom.map_mul, RingHom.map_mul, RingHom.map_pow, RingHom.map_pow, eval_X, mul_comm]
  rw [← tsub_add_cancel_of_le nN]
  rw [pow_add, mul_assoc, mul_comm (i b ^ n), mul_pow, mul_assoc, mul_assoc (i a ^ n), ← mul_pow]
  rw [bu, one_pow, mul_one]
#align denoms_clearable_C_mul_X_pow denomsClearable_C_mul_X_pow

/- warning: denoms_clearable.add -> DenomsClearable.add is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {K : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CommSemiring.{u2} K] {i : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))} {a : R} {b : R} {N : Nat} {f : Polynomial.{u1} R _inst_1} {g : Polynomial.{u1} R _inst_1}, (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N f i) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N g i) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N (HAdd.hAdd.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHAdd.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.add'.{u1} R _inst_1)) f g) i)
but is expected to have type
  forall {R : Type.{u2}} {K : Type.{u1}} [_inst_1 : Semiring.{u2} R] [_inst_2 : CommSemiring.{u1} K] {i : RingHom.{u2, u1} R K (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))} {a : R} {b : R} {N : Nat} {f : Polynomial.{u2} R _inst_1} {g : Polynomial.{u2} R _inst_1}, (DenomsClearable.{u2, u1} R K _inst_1 _inst_2 a b N f i) -> (DenomsClearable.{u2, u1} R K _inst_1 _inst_2 a b N g i) -> (DenomsClearable.{u2, u1} R K _inst_1 _inst_2 a b N (HAdd.hAdd.{u2, u2, u2} (Polynomial.{u2} R _inst_1) (Polynomial.{u2} R _inst_1) (Polynomial.{u2} R _inst_1) (instHAdd.{u2} (Polynomial.{u2} R _inst_1) (Polynomial.add'.{u2} R _inst_1)) f g) i)
Case conversion may be inaccurate. Consider using '#align denoms_clearable.add DenomsClearable.addₓ'. -/
theorem DenomsClearable.add {N : ℕ} {f g : R[X]} :
    DenomsClearable a b N f i → DenomsClearable a b N g i → DenomsClearable a b N (f + g) i :=
  fun ⟨Df, bf, bfu, Hf⟩ ⟨Dg, bg, bgu, Hg⟩ =>
  ⟨Df + Dg, bf, bfu,
    by
    rw [RingHom.map_add, Polynomial.map_add, eval_add, mul_add, Hf, Hg]
    congr
    refine' @inv_unique K _ (i b) bg bf _ _ <;> rwa [mul_comm]⟩
#align denoms_clearable.add DenomsClearable.add

/- warning: denoms_clearable_of_nat_degree_le -> denomsClearable_of_natDegree_le is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {K : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CommSemiring.{u2} K] {i : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))} {b : R} {bi : K} (N : Nat) (a : R), (Eq.{succ u2} K (HMul.hMul.{u2, u2, u2} K K K (instHMul.{u2} K (Distrib.toHasMul.{u2} K (NonUnitalNonAssocSemiring.toDistrib.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))))) bi (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) (fun (_x : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) => R -> K) (RingHom.hasCoeToFun.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) i b)) (OfNat.ofNat.{u2} K 1 (OfNat.mk.{u2} K 1 (One.one.{u2} K (AddMonoidWithOne.toOne.{u2} K (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} K (NonAssocSemiring.toAddCommMonoidWithOne.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))))))) -> (forall (f : Polynomial.{u1} R _inst_1), (LE.le.{0} Nat Nat.hasLe (Polynomial.natDegree.{u1} R _inst_1 f) N) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N f i))
but is expected to have type
  forall {R : Type.{u1}} {K : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CommSemiring.{u2} K] {i : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))} {b : R} {bi : K} (N : Nat) (a : R), (Eq.{succ u2} K (HMul.hMul.{u2, u2, u2} K ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => K) b) K (instHMul.{u2} K (NonUnitalNonAssocSemiring.toMul.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))) bi (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => K) _x) (MulHomClass.toFunLike.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))) (NonUnitalRingHomClass.toMulHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) (RingHomClass.toNonUnitalRingHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)) (RingHom.instRingHomClassRingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))))) i b)) (OfNat.ofNat.{u2} K 1 (One.toOfNat1.{u2} K (Semiring.toOne.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))) -> (forall (f : Polynomial.{u1} R _inst_1), (LE.le.{0} Nat instLENat (Polynomial.natDegree.{u1} R _inst_1 f) N) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b N f i))
Case conversion may be inaccurate. Consider using '#align denoms_clearable_of_nat_degree_le denomsClearable_of_natDegree_leₓ'. -/
theorem denomsClearable_of_natDegree_le (N : ℕ) (a : R) (bu : bi * i b = 1) :
    ∀ f : R[X], f.natDegree ≤ N → DenomsClearable a b N f i :=
  induction_with_natDegree_le _ N (denomsClearable_zero N a bu)
    (fun N_1 r r0 => denomsClearable_C_mul_X_pow a bu r) fun f g fg gN df dg => df.add dg
#align denoms_clearable_of_nat_degree_le denomsClearable_of_natDegree_le

/- warning: denoms_clearable_nat_degree -> denomsClearable_natDegree is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {K : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CommSemiring.{u2} K] {b : R} {bi : K} (i : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) (f : Polynomial.{u1} R _inst_1) (a : R), (Eq.{succ u2} K (HMul.hMul.{u2, u2, u2} K K K (instHMul.{u2} K (Distrib.toHasMul.{u2} K (NonUnitalNonAssocSemiring.toDistrib.{u2} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2)))))) bi (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) (fun (_x : RingHom.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) => R -> K) (RingHom.hasCoeToFun.{u1, u2} R K (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))) i b)) (OfNat.ofNat.{u2} K 1 (OfNat.mk.{u2} K 1 (One.one.{u2} K (AddMonoidWithOne.toOne.{u2} K (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} K (NonAssocSemiring.toAddCommMonoidWithOne.{u2} K (Semiring.toNonAssocSemiring.{u2} K (CommSemiring.toSemiring.{u2} K _inst_2))))))))) -> (DenomsClearable.{u1, u2} R K _inst_1 _inst_2 a b (Polynomial.natDegree.{u1} R _inst_1 f) f i)
but is expected to have type
  forall {R : Type.{u2}} {K : Type.{u1}} [_inst_1 : Semiring.{u2} R] [_inst_2 : CommSemiring.{u1} K] {b : R} {bi : K} (i : RingHom.{u2, u1} R K (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))) (f : Polynomial.{u2} R _inst_1) (a : R), (Eq.{succ u1} K (HMul.hMul.{u1, u1, u1} K ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => K) b) K (instHMul.{u1} K (NonUnitalNonAssocSemiring.toMul.{u1} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} K (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))))) bi (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (RingHom.{u2, u1} R K (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => K) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (RingHom.{u2, u1} R K (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))) R K (NonUnitalNonAssocSemiring.toMul.{u2} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R (Semiring.toNonAssocSemiring.{u2} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} K (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2)))) (NonUnitalRingHomClass.toMulHomClass.{max u2 u1, u2, u1} (RingHom.{u2, u1} R K (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))) R K (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R (Semiring.toNonAssocSemiring.{u2} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} K (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))) (RingHomClass.toNonUnitalRingHomClass.{max u2 u1, u2, u1} (RingHom.{u2, u1} R K (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))) R K (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2)) (RingHom.instRingHomClassRingHom.{u2, u1} R K (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2)))))) i b)) (OfNat.ofNat.{u1} K 1 (One.toOfNat1.{u1} K (Semiring.toOne.{u1} K (CommSemiring.toSemiring.{u1} K _inst_2))))) -> (DenomsClearable.{u2, u1} R K _inst_1 _inst_2 a b (Polynomial.natDegree.{u2} R _inst_1 f) f i)
Case conversion may be inaccurate. Consider using '#align denoms_clearable_nat_degree denomsClearable_natDegreeₓ'. -/
/-- If `i : R → K` is a ring homomorphism, `f` is a polynomial with coefficients in `R`,
`a, b` are elements of `R`, with `i b` invertible, then there is a `D ∈ R` such that
`b ^ f.nat_degree * f (a / b)` equals `i D`. -/
theorem denomsClearable_natDegree (i : R →+* K) (f : R[X]) (a : R) (bu : bi * i b = 1) :
    DenomsClearable a b f.natDegree f i :=
  denomsClearable_of_natDegree_le f.natDegree a bu f le_rfl
#align denoms_clearable_nat_degree denomsClearable_natDegree

end DenomsClearable

open RingHom

/- warning: one_le_pow_mul_abs_eval_div -> one_le_pow_mul_abs_eval_div is a dubious translation:
lean 3 declaration is
  forall {K : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} K] {f : Polynomial.{0} Int Int.semiring} {a : Int} {b : Int}, (LT.lt.{0} Int Int.hasLt (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) b) -> (Ne.{succ u1} K (Polynomial.eval.{u1} K (Ring.toSemiring.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) (HDiv.hDiv.{u1, u1, u1} K K K (instHDiv.{u1} K (DivInvMonoid.toHasDiv.{u1} K (DivisionRing.toDivInvMonoid.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Int K (HasLiftT.mk.{1, succ u1} Int K (CoeTCₓ.coe.{1, succ u1} Int K (Int.castCoe.{u1} K (AddGroupWithOne.toHasIntCast.{u1} K (AddCommGroupWithOne.toAddGroupWithOne.{u1} K (Ring.toAddCommGroupWithOne.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))))))) a) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Int K (HasLiftT.mk.{1, succ u1} Int K (CoeTCₓ.coe.{1, succ u1} Int K (Int.castCoe.{u1} K (AddGroupWithOne.toHasIntCast.{u1} K (AddCommGroupWithOne.toAddGroupWithOne.{u1} K (Ring.toAddCommGroupWithOne.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))))))) b)) (Polynomial.map.{0, u1} Int K Int.semiring (Ring.toSemiring.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) (algebraMap.{0, u1} Int K Int.commSemiring (Ring.toSemiring.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) (algebraInt.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))) f)) (OfNat.ofNat.{u1} K 0 (OfNat.mk.{u1} K 0 (Zero.zero.{u1} K (MulZeroClass.toHasZero.{u1} K (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} K (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} K (NonAssocRing.toNonUnitalNonAssocRing.{u1} K (Ring.toNonAssocRing.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))))))))))) -> (LE.le.{u1} K (Preorder.toLE.{u1} K (PartialOrder.toPreorder.{u1} K (OrderedAddCommGroup.toPartialOrder.{u1} K (StrictOrderedRing.toOrderedAddCommGroup.{u1} K (LinearOrderedRing.toStrictOrderedRing.{u1} K (LinearOrderedCommRing.toLinearOrderedRing.{u1} K (LinearOrderedField.toLinearOrderedCommRing.{u1} K _inst_1))))))) (OfNat.ofNat.{u1} K 1 (OfNat.mk.{u1} K 1 (One.one.{u1} K (AddMonoidWithOne.toOne.{u1} K (AddGroupWithOne.toAddMonoidWithOne.{u1} K (AddCommGroupWithOne.toAddGroupWithOne.{u1} K (Ring.toAddCommGroupWithOne.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))))))))) (HMul.hMul.{u1, u1, u1} K K K (instHMul.{u1} K (Distrib.toHasMul.{u1} K (Ring.toDistrib.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))))) (HPow.hPow.{u1, 0, u1} K Nat K (instHPow.{u1, 0} K Nat (Monoid.Pow.{u1} K (Ring.toMonoid.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Int K (HasLiftT.mk.{1, succ u1} Int K (CoeTCₓ.coe.{1, succ u1} Int K (Int.castCoe.{u1} K (AddGroupWithOne.toHasIntCast.{u1} K (AddCommGroupWithOne.toAddGroupWithOne.{u1} K (Ring.toAddCommGroupWithOne.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))))))) b) (Polynomial.natDegree.{0} Int Int.semiring f)) (Abs.abs.{u1} K (Neg.toHasAbs.{u1} K (SubNegMonoid.toHasNeg.{u1} K (AddGroup.toSubNegMonoid.{u1} K (AddGroupWithOne.toAddGroup.{u1} K (AddCommGroupWithOne.toAddGroupWithOne.{u1} K (Ring.toAddCommGroupWithOne.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))))))) (SemilatticeSup.toHasSup.{u1} K (Lattice.toSemilatticeSup.{u1} K (LinearOrder.toLattice.{u1} K (LinearOrderedRing.toLinearOrder.{u1} K (LinearOrderedCommRing.toLinearOrderedRing.{u1} K (LinearOrderedField.toLinearOrderedCommRing.{u1} K _inst_1))))))) (Polynomial.eval.{u1} K (Ring.toSemiring.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) (HDiv.hDiv.{u1, u1, u1} K K K (instHDiv.{u1} K (DivInvMonoid.toHasDiv.{u1} K (DivisionRing.toDivInvMonoid.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Int K (HasLiftT.mk.{1, succ u1} Int K (CoeTCₓ.coe.{1, succ u1} Int K (Int.castCoe.{u1} K (AddGroupWithOne.toHasIntCast.{u1} K (AddCommGroupWithOne.toAddGroupWithOne.{u1} K (Ring.toAddCommGroupWithOne.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))))))) a) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Int K (HasLiftT.mk.{1, succ u1} Int K (CoeTCₓ.coe.{1, succ u1} Int K (Int.castCoe.{u1} K (AddGroupWithOne.toHasIntCast.{u1} K (AddCommGroupWithOne.toAddGroupWithOne.{u1} K (Ring.toAddCommGroupWithOne.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))))))) b)) (Polynomial.map.{0, u1} Int K Int.semiring (Ring.toSemiring.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) (algebraMap.{0, u1} Int K Int.commSemiring (Ring.toSemiring.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) (algebraInt.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))) f)))))
but is expected to have type
  forall {K : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} K] {f : Polynomial.{0} Int Int.instSemiringInt} {a : Int} {b : Int}, (LT.lt.{0} Int Int.instLTInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) b) -> (Ne.{succ u1} K (Polynomial.eval.{u1} K (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (LinearOrderedSemifield.toSemifield.{u1} K (LinearOrderedField.toLinearOrderedSemifield.{u1} K _inst_1)))) (HDiv.hDiv.{u1, u1, u1} K K K (instHDiv.{u1} K (LinearOrderedField.toDiv.{u1} K _inst_1)) (Int.cast.{u1} K (Ring.toIntCast.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) a) (Int.cast.{u1} K (Ring.toIntCast.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) b)) (Polynomial.map.{0, u1} Int K (CommSemiring.toSemiring.{0} Int Int.instCommSemiringInt) (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (LinearOrderedSemifield.toSemifield.{u1} K (LinearOrderedField.toLinearOrderedSemifield.{u1} K _inst_1)))) (algebraMap.{0, u1} Int K Int.instCommSemiringInt (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (LinearOrderedSemifield.toSemifield.{u1} K (LinearOrderedField.toLinearOrderedSemifield.{u1} K _inst_1)))) (algebraInt.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))) f)) (OfNat.ofNat.{u1} K 0 (Zero.toOfNat0.{u1} K (CommMonoidWithZero.toZero.{u1} K (CommGroupWithZero.toCommMonoidWithZero.{u1} K (Semifield.toCommGroupWithZero.{u1} K (LinearOrderedSemifield.toSemifield.{u1} K (LinearOrderedField.toLinearOrderedSemifield.{u1} K _inst_1)))))))) -> (LE.le.{u1} K (Preorder.toLE.{u1} K (PartialOrder.toPreorder.{u1} K (StrictOrderedRing.toPartialOrder.{u1} K (LinearOrderedRing.toStrictOrderedRing.{u1} K (LinearOrderedCommRing.toLinearOrderedRing.{u1} K (LinearOrderedField.toLinearOrderedCommRing.{u1} K _inst_1)))))) (OfNat.ofNat.{u1} K 1 (One.toOfNat1.{u1} K (NonAssocRing.toOne.{u1} K (Ring.toNonAssocRing.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))))) (HMul.hMul.{u1, u1, u1} K K K (instHMul.{u1} K (NonUnitalNonAssocRing.toMul.{u1} K (NonAssocRing.toNonUnitalNonAssocRing.{u1} K (Ring.toNonAssocRing.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))))) (HPow.hPow.{u1, 0, u1} K Nat K (instHPow.{u1, 0} K Nat (Monoid.Pow.{u1} K (MonoidWithZero.toMonoid.{u1} K (Semiring.toMonoidWithZero.{u1} K (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (LinearOrderedSemifield.toSemifield.{u1} K (LinearOrderedField.toLinearOrderedSemifield.{u1} K _inst_1)))))))) (Int.cast.{u1} K (Ring.toIntCast.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) b) (Polynomial.natDegree.{0} Int Int.instSemiringInt f)) (Abs.abs.{u1} K (Neg.toHasAbs.{u1} K (Ring.toNeg.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) (SemilatticeSup.toSup.{u1} K (Lattice.toSemilatticeSup.{u1} K (DistribLattice.toLattice.{u1} K (instDistribLattice.{u1} K (LinearOrderedRing.toLinearOrder.{u1} K (LinearOrderedCommRing.toLinearOrderedRing.{u1} K (LinearOrderedField.toLinearOrderedCommRing.{u1} K _inst_1)))))))) (Polynomial.eval.{u1} K (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (LinearOrderedSemifield.toSemifield.{u1} K (LinearOrderedField.toLinearOrderedSemifield.{u1} K _inst_1)))) (HDiv.hDiv.{u1, u1, u1} K K K (instHDiv.{u1} K (LinearOrderedField.toDiv.{u1} K _inst_1)) (Int.cast.{u1} K (Ring.toIntCast.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) a) (Int.cast.{u1} K (Ring.toIntCast.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1)))) b)) (Polynomial.map.{0, u1} Int K (CommSemiring.toSemiring.{0} Int Int.instCommSemiringInt) (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (LinearOrderedSemifield.toSemifield.{u1} K (LinearOrderedField.toLinearOrderedSemifield.{u1} K _inst_1)))) (algebraMap.{0, u1} Int K Int.instCommSemiringInt (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (LinearOrderedSemifield.toSemifield.{u1} K (LinearOrderedField.toLinearOrderedSemifield.{u1} K _inst_1)))) (algebraInt.{u1} K (DivisionRing.toRing.{u1} K (Field.toDivisionRing.{u1} K (LinearOrderedField.toField.{u1} K _inst_1))))) f)))))
Case conversion may be inaccurate. Consider using '#align one_le_pow_mul_abs_eval_div one_le_pow_mul_abs_eval_divₓ'. -/
/-- Evaluating a polynomial with integer coefficients at a rational number and clearing
denominators, yields a number greater than or equal to one.  The target can be any
`linear_ordered_field K`.
The assumption on `K` could be weakened to `linear_ordered_comm_ring` assuming that the
image of the denominator is invertible in `K`. -/
theorem one_le_pow_mul_abs_eval_div {K : Type _} [LinearOrderedField K] {f : ℤ[X]} {a b : ℤ}
    (b0 : 0 < b) (fab : eval ((a : K) / b) (f.map (algebraMap ℤ K)) ≠ 0) :
    (1 : K) ≤ b ^ f.natDegree * |eval ((a : K) / b) (f.map (algebraMap ℤ K))| :=
  by
  obtain ⟨ev, bi, bu, hF⟩ :=
    @denomsClearable_natDegree _ _ _ _ b _ (algebraMap ℤ K) f a
      (by
        rw [eq_intCast, one_div_mul_cancel]
        rw [Int.cast_ne_zero]
        exact b0.ne.symm)
  obtain Fa := congr_arg abs hF
  rw [eq_one_div_of_mul_eq_one_left bu, eq_intCast, eq_intCast, abs_mul] at Fa
  rw [abs_of_pos (pow_pos (int.cast_pos.mpr b0) _ : 0 < (b : K) ^ _), one_div, eq_intCast] at Fa
  rw [div_eq_mul_inv, ← Fa, ← Int.cast_abs, ← Int.cast_one, Int.cast_le]
  refine' Int.le_of_lt_add_one ((lt_add_iff_pos_left 1).mpr (abs_pos.mpr fun F0 => fab _))
  rw [eq_one_div_of_mul_eq_one_left bu, F0, one_div, eq_intCast, Int.cast_zero, zero_eq_mul] at hF
  cases' hF with hF hF
  · exact (not_le.mpr b0 (le_of_eq (int.cast_eq_zero.mp (pow_eq_zero hF)))).elim
  · rwa [div_eq_mul_inv]
#align one_le_pow_mul_abs_eval_div one_le_pow_mul_abs_eval_div


/-
Copyright (c) 2019 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import data.matrix.basic
import data.pequiv

/-!
# partial equivalences for matrices

Using partial equivalences to represent matrices.
This file introduces the function `pequiv.to_matrix`, which returns a matrix containing ones and
zeros. For any partial equivalence `f`, `f.to_matrix i j = 1 ↔ f i = some j`.

The following important properties of this function are proved
`to_matrix_trans : (f.trans g).to_matrix = f.to_matrix ⬝ g.to_matrix`
`to_matrix_symm  : f.symm.to_matrix = f.to_matrixᵀ`
`to_matrix_refl : (pequiv.refl n).to_matrix = 1`
`to_matrix_bot : ⊥.to_matrix = 0`

This theory gives the matrix representation of projection linear maps, and their right inverses.
For example, the matrix `(single (0 : fin 1) (i : fin n)).to_matrix` corresponds to the the ith
projection map from R^n to R.

Any injective function `fin m → fin n` gives rise to a `pequiv`, whose matrix is the projection
map from R^m → R^n represented by the same function. The transpose of this matrix is the right
inverse of this map, sending anything not in the image to zero.

## notations

This file uses the notation ` ⬝ ` for `matrix.mul` and `ᵀ` for `matrix.transpose`.
-/

namespace pequiv
open matrix

universes u v

variables {k l m n : Type*}
variables [fintype k] [fintype l] [fintype m] [fintype n]
variables {α : Type v}

open_locale matrix

/-- `to_matrix` returns a matrix containing ones and zeros. `f.to_matrix i j` is `1` if
  `f i = some j` and `0` otherwise -/
def to_matrix [decidable_eq n] [has_zero α] [has_one α] (f : m ≃. n) : matrix m n α
| i j := if j ∈ f i then 1 else 0

lemma mul_matrix_apply [decidable_eq m] [semiring α] (f : l ≃. m) (M : matrix m n α) (i j) :
  (f.to_matrix ⬝ M) i j = option.cases_on (f i) 0 (λ fi, M fi j) :=
begin
  dsimp [to_matrix, matrix.mul_apply],
  cases h : f i with fi,
  { simp [h] },
  { rw finset.sum_eq_single fi;
    simp [h, eq_comm] {contextual := tt} }
end

lemma to_matrix_symm [decidable_eq m] [decidable_eq n] [has_zero α] [has_one α] (f : m ≃. n) :
  (f.symm.to_matrix : matrix n m α) = f.to_matrixᵀ :=
by ext; simp only [transpose, mem_iff_mem f, to_matrix]; congr

@[simp] lemma to_matrix_refl [decidable_eq n] [has_zero α] [has_one α] :
  ((pequiv.refl n).to_matrix : matrix n n α) = 1 :=
by ext; simp [to_matrix, one_apply]; congr

lemma matrix_mul_apply [semiring α] [decidable_eq n] (M : matrix l m α) (f : m ≃. n) (i j) :
  (M ⬝ f.to_matrix) i j = option.cases_on (f.symm j) 0 (λ fj, M i fj) :=
begin
  dsimp [to_matrix, matrix.mul_apply],
  cases h : f.symm j with fj,
  { simp [h, ← f.eq_some_iff] },
  { rw finset.sum_eq_single fj,
    { simp [h, ← f.eq_some_iff], },
    { intros b H n, simp [h, ← f.eq_some_iff, n.symm], },
    { simp, } }
end

lemma to_pequiv_mul_matrix [decidable_eq m] [semiring α] (f : m ≃ m) (M : matrix m n α) :
  (f.to_pequiv.to_matrix ⬝ M) = λ i, M (f i) :=
by { ext i j, rw [mul_matrix_apply, equiv.to_pequiv_apply] }

lemma to_matrix_trans [decidable_eq m] [decidable_eq n] [semiring α] (f : l ≃. m) (g : m ≃. n) :
  ((f.trans g).to_matrix : matrix l n α) = f.to_matrix ⬝ g.to_matrix :=
begin
  ext i j,
  rw [mul_matrix_apply],
  dsimp [to_matrix, pequiv.trans],
  cases f i; simp
end

@[simp] lemma to_matrix_bot [decidable_eq n] [has_zero α] [has_one α] :
  ((⊥ : pequiv m n).to_matrix : matrix m n α) = 0 := rfl

lemma to_matrix_injective [decidable_eq n] [monoid_with_zero α] [nontrivial α] :
  function.injective (@to_matrix m n _ _ α _ _ _) :=
begin
  classical,
  assume f g,
  refine not_imp_not.1 _,
  simp only [matrix.ext_iff.symm, to_matrix, pequiv.ext_iff,
    not_forall, exists_imp_distrib],
  assume i hi,
  use i,
  cases hf : f i with fi,
  { cases hg : g i with gi,
    { cc },
    { use gi,
      simp, } },
  { use fi,
    simp [hf.symm, ne.symm hi] }
end

lemma to_matrix_swap [decidable_eq n] [ring α] (i j : n) :
  (equiv.swap i j).to_pequiv.to_matrix =
  (1 : matrix n n α) - (single i i).to_matrix - (single j j).to_matrix + (single i j).to_matrix +
    (single j i).to_matrix :=
begin
  ext,
  dsimp [to_matrix, single, equiv.swap_apply_def, equiv.to_pequiv, one_apply],
  split_ifs; simp * at *
end

@[simp] lemma single_mul_single [decidable_eq k] [decidable_eq m] [decidable_eq n] [semiring α]
  (a : m) (b : n) (c : k) :
  ((single a b).to_matrix : matrix _ _ α) ⬝ (single b c).to_matrix = (single a c).to_matrix :=
by rw [← to_matrix_trans, single_trans_single]

lemma single_mul_single_of_ne [decidable_eq k] [decidable_eq m] [decidable_eq n] [semiring α]
  {b₁ b₂ : n} (hb : b₁ ≠ b₂) (a : m) (c : k) :
  ((single a b₁).to_matrix : matrix _ _ α) ⬝ (single b₂ c).to_matrix = 0 :=
by rw [← to_matrix_trans, single_trans_single_of_ne hb, to_matrix_bot]

/-- Restatement of `single_mul_single`, which will simplify expressions in `simp` normal form,
  when associativity may otherwise need to be carefully applied. -/
@[simp] lemma single_mul_single_right [decidable_eq k] [decidable_eq m] [decidable_eq n]
  [semiring α] (a : m) (b : n) (c : k) (M : matrix k l α) :
  (single a b).to_matrix ⬝ ((single b c).to_matrix ⬝ M) = (single a c).to_matrix ⬝ M :=
by rw [← matrix.mul_assoc, single_mul_single]

/-- We can also define permutation matrices by permuting the rows of the identity matrix. -/

end pequiv