/-
Copyright (c) 2021 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies

! This file was ported from Lean 3 source module data.int.succ_pred
! leanprover-community/mathlib commit e04043d6bf7264a3c84bc69711dc354958ca4516
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Int.Order.Basic
import Mathbin.Data.Nat.SuccPred

/-!
# Successors and predecessors of integers

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

In this file, we show that `ℤ` is both an archimedean `succ_order` and an archimedean `pred_order`.
-/

open Function Order

namespace Int

-- so that Lean reads `int.succ` through `succ_order.succ`
@[reducible]
instance : SuccOrder ℤ :=
  { SuccOrder.ofSuccLeIff succ fun a b => Iff.rfl with succ := succ }

-- so that Lean reads `int.pred` through `pred_order.pred`
@[reducible]
instance : PredOrder ℤ where
  pred := pred
  pred_le a := (sub_one_lt_of_le le_rfl).le
  min_of_le_pred a ha := ((sub_one_lt_of_le le_rfl).not_le ha).elim
  le_pred_of_lt a b := le_sub_one_of_lt
  le_of_pred_lt a b := le_of_sub_one_lt

/- warning: int.succ_eq_succ -> Int.succ_eq_succ is a dubious translation:
lean 3 declaration is
  Eq.{1} (Int -> Int) (Order.succ.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.succOrder) Int.succ
but is expected to have type
  Eq.{1} (Int -> Int) (Order.succ.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) Int.instSuccOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing) Int.succ
Case conversion may be inaccurate. Consider using '#align int.succ_eq_succ Int.succ_eq_succₓ'. -/
@[simp]
theorem succ_eq_succ : Order.succ = succ :=
  rfl
#align int.succ_eq_succ Int.succ_eq_succ

#print Int.pred_eq_pred /-
@[simp]
theorem pred_eq_pred : Order.pred = pred :=
  rfl
#align int.pred_eq_pred Int.pred_eq_pred
-/

#print Int.pos_iff_one_le /-
theorem pos_iff_one_le {a : ℤ} : 0 < a ↔ 1 ≤ a :=
  Order.succ_le_iff.symm
#align int.pos_iff_one_le Int.pos_iff_one_le
-/

#print Int.succ_iterate /-
theorem succ_iterate (a : ℤ) : ∀ n, (succ^[n]) a = a + n
  | 0 => (add_zero a).symm
  | n + 1 => by
    rw [Function.iterate_succ', Int.ofNat_succ, ← add_assoc]
    exact congr_arg _ (succ_iterate n)
#align int.succ_iterate Int.succ_iterate
-/

#print Int.pred_iterate /-
theorem pred_iterate (a : ℤ) : ∀ n, (pred^[n]) a = a - n
  | 0 => (sub_zero a).symm
  | n + 1 => by
    rw [Function.iterate_succ', Int.ofNat_succ, ← sub_sub]
    exact congr_arg _ (pred_iterate n)
#align int.pred_iterate Int.pred_iterate
-/

instance : IsSuccArchimedean ℤ :=
  ⟨fun a b h =>
    ⟨(b - a).toNat, by
      rw [succ_eq_succ, succ_iterate, to_nat_sub_of_le h, ← add_sub_assoc, add_sub_cancel']⟩⟩

instance : IsPredArchimedean ℤ :=
  ⟨fun a b h =>
    ⟨(b - a).toNat, by rw [pred_eq_pred, pred_iterate, to_nat_sub_of_le h, sub_sub_cancel]⟩⟩

/-! ### Covering relation -/

#print Int.covby_iff_succ_eq /-
protected theorem covby_iff_succ_eq {m n : ℤ} : m ⋖ n ↔ m + 1 = n :=
  succ_eq_iff_covby.symm
#align int.covby_iff_succ_eq Int.covby_iff_succ_eq
-/

#print Int.sub_one_covby /-
@[simp]
theorem sub_one_covby (z : ℤ) : z - 1 ⋖ z := by rw [Int.covby_iff_succ_eq, sub_add_cancel]
#align int.sub_one_covby Int.sub_one_covby
-/

#print Int.covby_add_one /-
@[simp]
theorem covby_add_one (z : ℤ) : z ⋖ z + 1 :=
  Int.covby_iff_succ_eq.mpr rfl
#align int.covby_add_one Int.covby_add_one
-/

end Int

#print Nat.cast_int_covby_iff /-
@[simp, norm_cast]
theorem Nat.cast_int_covby_iff {a b : ℕ} : (a : ℤ) ⋖ b ↔ a ⋖ b :=
  by
  rw [Nat.covby_iff_succ_eq, Int.covby_iff_succ_eq]
  exact Int.coe_nat_inj'
#align nat.cast_int_covby_iff Nat.cast_int_covby_iff
-/

alias Nat.cast_int_covby_iff ↔ _ Covby.cast_int
#align covby.cast_int Covby.cast_int


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Johannes Hölzl, Scott Morrison, Jens Wagemaker

! This file was ported from Lean 3 source module data.polynomial.monic
! leanprover-community/mathlib commit cbdf7b565832144d024caa5a550117c6df0204a5
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Polynomial.Reverse
import Mathlib.Algebra.Regular.SMul

/-!
# Theory of monic polynomials

We give several tools for proving that polynomials are monic, e.g.
`Monic.mul`, `Monic.map`, `Monic.pow`.
-/

noncomputable section

open Finset

open BigOperators Classical Polynomial

namespace Polynomial

universe u v y

variable {R : Type u} {S : Type v} {a b : R} {m n : ℕ} {ι : Type y}

section Semiring

variable [Semiring R] {p q r : R[X]}

theorem monic_zero_iff_subsingleton : Monic (0 : R[X]) ↔ Subsingleton R :=
  subsingleton_iff_zero_eq_one
#align polynomial.monic_zero_iff_subsingleton Polynomial.monic_zero_iff_subsingleton

theorem not_monic_zero_iff : ¬Monic (0 : R[X]) ↔ (0 : R) ≠ 1 :=
  (monic_zero_iff_subsingleton.trans subsingleton_iff_zero_eq_one.symm).not
#align polynomial.not_monic_zero_iff Polynomial.not_monic_zero_iff

theorem monic_zero_iff_subsingleton' :
    Monic (0 : R[X]) ↔ (∀ f g : R[X], f = g) ∧ ∀ a b : R, a = b :=
  Polynomial.monic_zero_iff_subsingleton.trans
    ⟨by
      intro
      simp, fun h => subsingleton_iff.mpr h.2⟩
#align polynomial.monic_zero_iff_subsingleton' Polynomial.monic_zero_iff_subsingleton'

theorem Monic.as_sum (hp : p.Monic) :
    p = X ^ p.natDegree + ∑ i in range p.natDegree, C (p.coeff i) * X ^ i := by
  conv_lhs => rw [p.as_sum_range_C_mul_X_pow, sum_range_succ_comm]
  suffices C (p.coeff p.natDegree) = 1 by rw [this, one_mul]
  exact congr_arg C hp
#align polynomial.monic.as_sum Polynomial.Monic.as_sum

theorem ne_zero_of_ne_zero_of_monic (hp : p ≠ 0) (hq : Monic q) : q ≠ 0 := by
  rintro rfl
  rw [Monic.def, leadingCoeff_zero] at hq
  rw [← mul_one p, ← C_1, ← hq, C_0, mul_zero] at hp
  exact hp rfl
#align polynomial.ne_zero_of_ne_zero_of_monic Polynomial.ne_zero_of_ne_zero_of_monic

theorem Monic.map [Semiring S] (f : R →+* S) (hp : Monic p) : Monic (p.map f) := by
  unfold Monic
  nontriviality
  have : f p.leadingCoeff ≠ 0 :=
    by
    rw [show _ = _ from hp, f.map_one]
    exact one_ne_zero
  rw [Polynomial.leadingCoeff, coeff_map]
  suffices p.coeff (p.map f).natDegree = 1 by simp [this]
  rwa [natDegree_eq_of_degree_eq (degree_map_eq_of_leadingCoeff_ne_zero f this)]
#align polynomial.monic.map Polynomial.Monic.map

theorem monic_C_mul_of_mul_leadingCoeff_eq_one {b : R} (hp : b * p.leadingCoeff = 1) :
    Monic (C b * p) := by
  unfold Monic
  nontriviality
  rw [leadingCoeff_mul' _] <;> simp [leadingCoeff_C b, hp]
set_option linter.uppercaseLean3 false in
#align polynomial.monic_C_mul_of_mul_leading_coeff_eq_one Polynomial.monic_C_mul_of_mul_leadingCoeff_eq_one

theorem monic_mul_C_of_leadingCoeff_mul_eq_one {b : R} (hp : p.leadingCoeff * b = 1) :
    Monic (p * C b) := by
  unfold Monic
  nontriviality
  rw [leadingCoeff_mul' _] <;> simp [leadingCoeff_C b, hp]
set_option linter.uppercaseLean3 false in
#align polynomial.monic_mul_C_of_leading_coeff_mul_eq_one Polynomial.monic_mul_C_of_leadingCoeff_mul_eq_one

theorem monic_of_degree_le (n : ℕ) (H1 : degree p ≤ n) (H2 : coeff p n = 1) : Monic p :=
  Decidable.byCases
    (fun H : degree p < n => eq_of_zero_eq_one (H2 ▸ (coeff_eq_zero_of_degree_lt H).symm) _ _)
    fun H : ¬degree p < n => by
    rwa [Monic, Polynomial.leadingCoeff, natDegree, (lt_or_eq_of_le H1).resolve_left H]
#align polynomial.monic_of_degree_le Polynomial.monic_of_degree_le

theorem monic_X_pow_add {n : ℕ} (H : degree p ≤ n) : Monic (X ^ (n + 1) + p) :=
  have H1 : degree p < (n + 1 : ℕ) := lt_of_le_of_lt H (WithBot.coe_lt_coe.2 (Nat.lt_succ_self n))
  monic_of_degree_le (n + 1)
    (le_trans (degree_add_le _ _) (max_le (degree_X_pow_le _) (le_of_lt H1)))
    (by rw [coeff_add, coeff_X_pow, if_pos rfl, coeff_eq_zero_of_degree_lt H1, add_zero])
set_option linter.uppercaseLean3 false in
#align polynomial.monic_X_pow_add Polynomial.monic_X_pow_add

theorem monic_X_add_C (x : R) : Monic (X + C x) :=
  pow_one (X : R[X]) ▸ monic_X_pow_add degree_C_le
set_option linter.uppercaseLean3 false in
#align polynomial.monic_X_add_C Polynomial.monic_X_add_C

theorem Monic.mul (hp : Monic p) (hq : Monic q) : Monic (p * q) :=
  if h0 : (0 : R) = 1 then
    haveI := subsingleton_of_zero_eq_one h0
    Subsingleton.elim _ _
  else by
    have : p.leadingCoeff * q.leadingCoeff ≠ 0 := by
      simp [Monic.def.1 hp, Monic.def.1 hq, Ne.symm h0]
    rw [Monic.def, leadingCoeff_mul' this, Monic.def.1 hp, Monic.def.1 hq, one_mul]
#align polynomial.monic.mul Polynomial.Monic.mul

theorem Monic.pow (hp : Monic p) : ∀ n : ℕ, Monic (p ^ n)
  | 0 => monic_one
  | n + 1 => by
    rw [pow_succ]
    exact hp.mul (Monic.pow hp n)
#align polynomial.monic.pow Polynomial.Monic.pow

theorem Monic.add_of_left (hp : Monic p) (hpq : degree q < degree p) : Monic (p + q) := by
  rwa [Monic, add_comm, leadingCoeff_add_of_degree_lt hpq]
#align polynomial.monic.add_of_left Polynomial.Monic.add_of_left

theorem Monic.add_of_right (hq : Monic q) (hpq : degree p < degree q) : Monic (p + q) := by
  rwa [Monic, leadingCoeff_add_of_degree_lt hpq]
#align polynomial.monic.add_of_right Polynomial.Monic.add_of_right

theorem Monic.of_mul_monic_left (hp : p.Monic) (hpq : (p * q).Monic) : q.Monic := by
  contrapose! hpq
  rw [Monic.def] at hpq⊢
  rwa [leadingCoeff_monic_mul hp]
#align polynomial.monic.of_mul_monic_left Polynomial.Monic.of_mul_monic_left

theorem Monic.of_mul_monic_right (hq : q.Monic) (hpq : (p * q).Monic) : p.Monic := by
  contrapose! hpq
  rw [Monic.def] at hpq⊢
  rwa [leadingCoeff_mul_monic hq]
#align polynomial.monic.of_mul_monic_right Polynomial.Monic.of_mul_monic_right

namespace Monic

@[simp]
theorem natDegree_eq_zero_iff_eq_one (hp : p.Monic) : p.natDegree = 0 ↔ p = 1 := by
  constructor <;> intro h
  swap
  · rw [h]
    exact natDegree_one
  have : p = C (p.coeff 0) := by
    rw [← Polynomial.degree_le_zero_iff]
    rwa [Polynomial.natDegree_eq_zero_iff_degree_le_zero] at h
  rw [this]
  rw [← h, ← Polynomial.leadingCoeff, Monic.def.1 hp, C_1]
#align polynomial.monic.nat_degree_eq_zero_iff_eq_one Polynomial.Monic.natDegree_eq_zero_iff_eq_one

@[simp]
theorem degree_le_zero_iff_eq_one (hp : p.Monic) : p.degree ≤ 0 ↔ p = 1 := by
  rw [← hp.natDegree_eq_zero_iff_eq_one, natDegree_eq_zero_iff_degree_le_zero]
#align polynomial.monic.degree_le_zero_iff_eq_one Polynomial.Monic.degree_le_zero_iff_eq_one

theorem natDegree_mul (hp : p.Monic) (hq : q.Monic) :
    (p * q).natDegree = p.natDegree + q.natDegree := by
  nontriviality R
  apply natDegree_mul'
  simp [hp.leadingCoeff, hq.leadingCoeff]
#align polynomial.monic.nat_degree_mul Polynomial.Monic.natDegree_mul

theorem degree_mul_comm (hp : p.Monic) (q : R[X]) : (p * q).degree = (q * p).degree := by
  by_cases h : q = 0
  · simp [h]
  rw [degree_mul', hp.degree_mul]
  · exact add_comm _ _
  · rwa [hp.leadingCoeff, one_mul, leadingCoeff_ne_zero]
#align polynomial.monic.degree_mul_comm Polynomial.Monic.degree_mul_comm

nonrec theorem natDegree_mul' (hp : p.Monic) (hq : q ≠ 0) :
    (p * q).natDegree = p.natDegree + q.natDegree := by
  rw [natDegree_mul', add_comm]
  simpa [hp.leadingCoeff, leadingCoeff_ne_zero]
#align polynomial.monic.nat_degree_mul' Polynomial.Monic.natDegree_mul'

theorem natDegree_mul_comm (hp : p.Monic) (q : R[X]) : (p * q).natDegree = (q * p).natDegree := by
  by_cases h : q = 0
  · simp [h]
  rw [hp.natDegree_mul' h, Polynomial.natDegree_mul', add_comm]
  simpa [hp.leadingCoeff, leadingCoeff_ne_zero]
#align polynomial.monic.nat_degree_mul_comm Polynomial.Monic.natDegree_mul_comm

theorem not_dvd_of_natDegree_lt (hp : Monic p) (h0 : q ≠ 0) (hl : natDegree q < natDegree p) :
    ¬p ∣ q := by
  rintro ⟨r, rfl⟩
  rw [hp.natDegree_mul' <| right_ne_zero_of_mul h0] at hl
  exact hl.not_le (Nat.le_add_right _ _)
#align polynomial.monic.not_dvd_of_nat_degree_lt Polynomial.Monic.not_dvd_of_natDegree_lt

theorem not_dvd_of_degree_lt (hp : Monic p) (h0 : q ≠ 0) (hl : degree q < degree p) : ¬p ∣ q :=
  Monic.not_dvd_of_natDegree_lt hp h0 <| natDegree_lt_natDegree h0 hl
#align polynomial.monic.not_dvd_of_degree_lt Polynomial.Monic.not_dvd_of_degree_lt

theorem nextCoeff_mul (hp : Monic p) (hq : Monic q) :
    nextCoeff (p * q) = nextCoeff p + nextCoeff q := by
  nontriviality
  simp only [← coeff_one_reverse]
  rw [reverse_mul] <;>
    simp [coeff_mul, Nat.antidiagonal, hp.leadingCoeff, hq.leadingCoeff, add_comm,
      show Nat.succ 0 = 1 from rfl]
#align polynomial.monic.next_coeff_mul Polynomial.Monic.nextCoeff_mul

theorem natDegree_pow (hp : p.Monic) (n : ℕ) : (p ^ n).natDegree = n * p.natDegree := by
  induction' n with n hn
  · simp
  · rw [pow_succ, hp.natDegree_mul (hp.pow n), hn, Nat.succ_mul, add_comm]
#align polynomial.monic.nat_degree_pow Polynomial.Monic.natDegree_pow

end Monic

@[simp]
theorem natDegree_pow_X_add_c [Nontrivial R] (n : ℕ) (r : R) : ((X + C r) ^ n).natDegree = n := by
  rw [(monic_X_add_C r).natDegree_pow, natDegree_X_add_C, mul_one]
set_option linter.uppercaseLean3 false in
#align polynomial.nat_degree_pow_X_add_C Polynomial.natDegree_pow_X_add_c

theorem Monic.eq_one_of_isUnit (hm : Monic p) (hpu : IsUnit p) : p = 1 := by
  nontriviality R
  obtain ⟨q, h⟩ := hpu.exists_right_inv
  have := hm.natDegree_mul' (right_ne_zero_of_mul_eq_one h)
  rw [h, natDegree_one, eq_comm, add_eq_zero_iff] at this
  exact hm.natDegree_eq_zero_iff_eq_one.mp this.1
#align polynomial.monic.eq_one_of_is_unit Polynomial.Monic.eq_one_of_isUnit

theorem Monic.isUnit_iff (hm : p.Monic) : IsUnit p ↔ p = 1 :=
  ⟨hm.eq_one_of_isUnit, fun h => h.symm ▸ isUnit_one⟩
#align polynomial.monic.is_unit_iff Polynomial.Monic.isUnit_iff

end Semiring

section CommSemiring

variable [CommSemiring R] {p : R[X]}

theorem monic_multiset_prod_of_monic (t : Multiset ι) (f : ι → R[X]) (ht : ∀ i ∈ t, Monic (f i)) :
    Monic (t.map f).prod := by
  revert ht
  refine' t.induction_on _ _; · simp
  intro a t ih ht
  rw [Multiset.map_cons, Multiset.prod_cons]
  exact (ht _ (Multiset.mem_cons_self _ _)).mul (ih fun _ hi => ht _ (Multiset.mem_cons_of_mem hi))
#align polynomial.monic_multiset_prod_of_monic Polynomial.monic_multiset_prod_of_monic

theorem monic_prod_of_monic (s : Finset ι) (f : ι → R[X]) (hs : ∀ i ∈ s, Monic (f i)) :
    Monic (∏ i in s, f i) :=
  monic_multiset_prod_of_monic s.1 f hs
#align polynomial.monic_prod_of_monic Polynomial.monic_prod_of_monic

theorem Monic.nextCoeff_multiset_prod (t : Multiset ι) (f : ι → R[X]) (h : ∀ i ∈ t, Monic (f i)) :
    nextCoeff (t.map f).prod = (t.map fun i => nextCoeff (f i)).sum := by
  revert h
  refine' Multiset.induction_on t _ fun a t ih ht => _
  · simp only [Multiset.not_mem_zero, forall_prop_of_true, forall_prop_of_false, Multiset.map_zero,
      Multiset.prod_zero, Multiset.sum_zero, not_false_iff, forall_true_iff]
    rw [← C_1]
    rw [nextCoeff_C_eq_zero]
  · rw [Multiset.map_cons, Multiset.prod_cons, Multiset.map_cons, Multiset.sum_cons,
      Monic.nextCoeff_mul, ih]
    exacts[fun i hi => ht i (Multiset.mem_cons_of_mem hi), ht a (Multiset.mem_cons_self _ _),
      monic_multiset_prod_of_monic _ _ fun b bs => ht _ (Multiset.mem_cons_of_mem bs)]
#align polynomial.monic.next_coeff_multiset_prod Polynomial.Monic.nextCoeff_multiset_prod

theorem Monic.nextCoeff_prod (s : Finset ι) (f : ι → R[X]) (h : ∀ i ∈ s, Monic (f i)) :
    nextCoeff (∏ i in s, f i) = ∑ i in s, nextCoeff (f i) :=
  Monic.nextCoeff_multiset_prod s.1 f h
#align polynomial.monic.next_coeff_prod Polynomial.Monic.nextCoeff_prod

end CommSemiring

section Semiring

variable [Semiring R]

@[simp]
theorem Monic.natDegree_map [Semiring S] [Nontrivial S] {P : R[X]} (hmo : P.Monic) (f : R →+* S) :
    (P.map f).natDegree = P.natDegree := by
  refine' le_antisymm (natDegree_map_le _ _) (le_natDegree_of_ne_zero _)
  rw [coeff_map, Monic.coeff_natDegree hmo, RingHom.map_one]
  exact one_ne_zero
#align polynomial.monic.nat_degree_map Polynomial.Monic.natDegree_map

@[simp]
theorem Monic.degree_map [Semiring S] [Nontrivial S] {P : R[X]} (hmo : P.Monic) (f : R →+* S) :
    (P.map f).degree = P.degree := by
  by_cases hP : P = 0
  · simp [hP]
  · refine' le_antisymm (degree_map_le _ _) _
    rw [degree_eq_natDegree hP]
    refine' le_degree_of_ne_zero _
    rw [coeff_map, Monic.coeff_natDegree hmo, RingHom.map_one]
    exact one_ne_zero
#align polynomial.monic.degree_map Polynomial.Monic.degree_map

section Injective

open Function

variable [Semiring S] {f : R →+* S} (hf : Injective f)

theorem degree_map_eq_of_injective (p : R[X]) : degree (p.map f) = degree p :=
  if h : p = 0 then by simp [h]
  else
    degree_map_eq_of_leadingCoeff_ne_zero _
      (by rw [← f.map_zero]; exact mt hf.eq_iff.1 (mt leadingCoeff_eq_zero.1 h))
#align polynomial.degree_map_eq_of_injective Polynomial.degree_map_eq_of_injective

theorem natDegree_map_eq_of_injective (p : R[X]) : natDegree (p.map f) = natDegree p :=
  natDegree_eq_of_degree_eq (degree_map_eq_of_injective hf p)
#align polynomial.nat_degree_map_eq_of_injective Polynomial.natDegree_map_eq_of_injective

theorem leadingCoeff_map' (p : R[X]) : leadingCoeff (p.map f) = f (leadingCoeff p) := by
  unfold leadingCoeff
  rw [coeff_map, natDegree_map_eq_of_injective hf p]
#align polynomial.leading_coeff_map' Polynomial.leadingCoeff_map'

theorem nextCoeff_map (p : R[X]) : (p.map f).nextCoeff = f p.nextCoeff := by
  unfold nextCoeff
  rw [natDegree_map_eq_of_injective hf]
  split_ifs <;> simp [*]
#align polynomial.next_coeff_map Polynomial.nextCoeff_map

theorem leadingCoeff_of_injective (p : R[X]) : leadingCoeff (p.map f) = f (leadingCoeff p) := by
  delta leadingCoeff
  rw [coeff_map f, natDegree_map_eq_of_injective hf p]
#align polynomial.leading_coeff_of_injective Polynomial.leadingCoeff_of_injective

theorem monic_of_injective {p : R[X]} (hp : (p.map f).Monic) : p.Monic := by
  apply hf
  rw [← leadingCoeff_of_injective hf, hp.leadingCoeff, f.map_one]
#align polynomial.monic_of_injective Polynomial.monic_of_injective

theorem _root_.Function.Injective.monic_map_iff {p : R[X]} : p.Monic ↔ (p.map f).Monic :=
  ⟨Monic.map _, Polynomial.monic_of_injective hf⟩
#align function.injective.monic_map_iff Function.Injective.monic_map_iff

end Injective

end Semiring

section Ring

variable [Ring R] {p : R[X]}

theorem monic_X_sub_C (x : R) : Monic (X - C x) := by
  simpa only [sub_eq_add_neg, C_neg] using monic_X_add_C (-x)
set_option linter.uppercaseLean3 false in
#align polynomial.monic_X_sub_C Polynomial.monic_X_sub_C

theorem monic_X_pow_sub {n : ℕ} (H : degree p ≤ n) : Monic (X ^ (n + 1) - p) := by
  simpa [sub_eq_add_neg] using monic_X_pow_add (show degree (-p) ≤ n by rwa [← degree_neg p] at H)
set_option linter.uppercaseLean3 false in
#align polynomial.monic_X_pow_sub Polynomial.monic_X_pow_sub

/-- `X ^ n - a` is monic. -/
theorem monic_X_pow_sub_C {R : Type u} [Ring R] (a : R) {n : ℕ} (h : n ≠ 0) : (X ^ n - C a).Monic :=
  by
  obtain ⟨k, rfl⟩ := Nat.exists_eq_succ_of_ne_zero h
  apply monic_X_pow_sub _
  exact le_trans degree_C_le Nat.WithBot.coe_nonneg
set_option linter.uppercaseLean3 false in
#align polynomial.monic_X_pow_sub_C Polynomial.monic_X_pow_sub_C

theorem not_isUnit_X_pow_sub_one (R : Type _) [CommRing R] [Nontrivial R] (n : ℕ) :
    ¬IsUnit (X ^ n - 1 : R[X]) := by
  intro h
  rcases eq_or_ne n 0 with (rfl | hn)
  · simp at h
  apply hn
  rw [← @natDegree_one R, ← (monic_X_pow_sub_C _ hn).eq_one_of_isUnit h, natDegree_X_pow_sub_C]
set_option linter.uppercaseLean3 false in
#align polynomial.not_is_unit_X_pow_sub_one Polynomial.not_isUnit_X_pow_sub_one

theorem Monic.sub_of_left {p q : R[X]} (hp : Monic p) (hpq : degree q < degree p) : Monic (p - q) :=
  by
  rw [sub_eq_add_neg]
  apply hp.add_of_left
  rwa [degree_neg]
#align polynomial.monic.sub_of_left Polynomial.Monic.sub_of_left

theorem Monic.sub_of_right {p q : R[X]} (hq : q.leadingCoeff = -1) (hpq : degree p < degree q) :
    Monic (p - q) := by
  have : (-q).coeff (-q).natDegree = 1 := by
    rw [natDegree_neg, coeff_neg, show q.coeff q.natDegree = -1 from hq, neg_neg]
  rw [sub_eq_add_neg]
  apply Monic.add_of_right this
  rwa [degree_neg]
#align polynomial.monic.sub_of_right Polynomial.Monic.sub_of_right

end Ring

section NonzeroSemiring

variable [Semiring R] [Nontrivial R] {p q : R[X]}

@[simp]
theorem not_monic_zero : ¬Monic (0 : R[X]) :=
  not_monic_zero_iff.mp zero_ne_one
#align polynomial.not_monic_zero Polynomial.not_monic_zero

end NonzeroSemiring

section NotZeroDivisor

-- TODO: using gh-8537, rephrase lemmas that involve commutation around `*` using the op-ring
variable [Semiring R] {p : R[X]}

theorem Monic.mul_left_ne_zero (hp : Monic p) {q : R[X]} (hq : q ≠ 0) : q * p ≠ 0 := by
  by_cases h : p = 1
  · simpa [h]
  rw [Ne.def, ← degree_eq_bot, hp.degree_mul, WithBot.add_eq_bot, not_or, degree_eq_bot]
  refine' ⟨hq, _⟩
  rw [← hp.degree_le_zero_iff_eq_one, not_le] at h
  refine' (lt_trans _ h).ne'
  simp
#align polynomial.monic.mul_left_ne_zero Polynomial.Monic.mul_left_ne_zero

theorem Monic.mul_right_ne_zero (hp : Monic p) {q : R[X]} (hq : q ≠ 0) : p * q ≠ 0 := by
  by_cases h : p = 1
  · simpa [h]
  rw [Ne.def, ← degree_eq_bot, hp.degree_mul_comm, hp.degree_mul, WithBot.add_eq_bot, not_or,
    degree_eq_bot]
  refine' ⟨hq, _⟩
  rw [← hp.degree_le_zero_iff_eq_one, not_le] at h
  refine' (lt_trans _ h).ne'
  simp
#align polynomial.monic.mul_right_ne_zero Polynomial.Monic.mul_right_ne_zero

theorem Monic.mul_natDegree_lt_iff (h : Monic p) {q : R[X]} :
    (p * q).natDegree < p.natDegree ↔ p ≠ 1 ∧ q = 0 := by
  by_cases hq : q = 0
  · suffices 0 < p.natDegree ↔ p.natDegree ≠ 0 by simpa [hq, ← h.natDegree_eq_zero_iff_eq_one]
    exact ⟨fun h => h.ne', fun h => lt_of_le_of_ne (Nat.zero_le _) h.symm⟩
  · simp [h.natDegree_mul', hq]
#align polynomial.monic.mul_nat_degree_lt_iff Polynomial.Monic.mul_natDegree_lt_iff

theorem Monic.mul_right_eq_zero_iff (h : Monic p) {q : R[X]} : p * q = 0 ↔ q = 0 := by
  by_cases hq : q = 0 <;> simp [h.mul_right_ne_zero, hq]
#align polynomial.monic.mul_right_eq_zero_iff Polynomial.Monic.mul_right_eq_zero_iff

theorem Monic.mul_left_eq_zero_iff (h : Monic p) {q : R[X]} : q * p = 0 ↔ q = 0 := by
  by_cases hq : q = 0 <;> simp [h.mul_left_ne_zero, hq]
#align polynomial.monic.mul_left_eq_zero_iff Polynomial.Monic.mul_left_eq_zero_iff

theorem Monic.isRegular {R : Type _} [Ring R] {p : R[X]} (hp : Monic p) : IsRegular p := by
  constructor
  · intro q r h
    dsimp only at h
    rw [← sub_eq_zero, ← hp.mul_right_eq_zero_iff, mul_sub, h, sub_self]
  · intro q r h
    simp only at h
    rw [← sub_eq_zero, ← hp.mul_left_eq_zero_iff, sub_mul, h, sub_self]
#align polynomial.monic.is_regular Polynomial.Monic.isRegular

theorem degree_smul_of_smul_regular {S : Type _} [Monoid S] [DistribMulAction S R] {k : S}
    (p : R[X]) (h : IsSMulRegular R k) : (k • p).degree = p.degree := by
  refine' le_antisymm _ _
  · rw [degree_le_iff_coeff_zero]
    intro m hm
    rw [degree_lt_iff_coeff_zero] at hm
    simp [hm m le_rfl]
  · rw [degree_le_iff_coeff_zero]
    intro m hm
    rw [degree_lt_iff_coeff_zero] at hm
    refine' h _
    simpa using hm m le_rfl
#align polynomial.degree_smul_of_smul_regular Polynomial.degree_smul_of_smul_regular

theorem natDegree_smul_of_smul_regular {S : Type _} [Monoid S] [DistribMulAction S R] {k : S}
    (p : R[X]) (h : IsSMulRegular R k) : (k • p).natDegree = p.natDegree := by
  by_cases hp : p = 0
  · simp [hp]
  rw [← WithBot.coe_eq_coe, ← Nat.cast_withBot, ←Nat.cast_withBot,
      ← degree_eq_natDegree hp, ← degree_eq_natDegree,
    degree_smul_of_smul_regular p h]
  contrapose! hp
  rw [← smul_zero k] at hp
  exact h.polynomial hp
#align polynomial.nat_degree_smul_of_smul_regular Polynomial.natDegree_smul_of_smul_regular

theorem leadingCoeff_smul_of_smul_regular {S : Type _} [Monoid S] [DistribMulAction S R] {k : S}
    (p : R[X]) (h : IsSMulRegular R k) : (k • p).leadingCoeff = k • p.leadingCoeff := by
  rw [Polynomial.leadingCoeff, Polynomial.leadingCoeff, coeff_smul,
    natDegree_smul_of_smul_regular p h]
#align polynomial.leading_coeff_smul_of_smul_regular Polynomial.leadingCoeff_smul_of_smul_regular

theorem monic_of_isUnit_leadingCoeff_inv_smul (h : IsUnit p.leadingCoeff) : Monic (h.unit⁻¹ • p) :=
  by
  rw [Monic.def, leadingCoeff_smul_of_smul_regular _ (isSMulRegular_of_group _), Units.smul_def]
  obtain ⟨k, hk⟩ := h
  simp only [← hk, smul_eq_mul, ← Units.val_mul, Units.val_eq_one, inv_mul_eq_iff_eq_mul]
  simp [Units.ext_iff, IsUnit.unit_spec]
#align polynomial.monic_of_is_unit_leading_coeff_inv_smul Polynomial.monic_of_isUnit_leadingCoeff_inv_smul

theorem isUnit_leadingCoeff_mul_right_eq_zero_iff (h : IsUnit p.leadingCoeff) {q : R[X]} :
    p * q = 0 ↔ q = 0 := by
  constructor
  · intro hp
    rw [← smul_eq_zero_iff_eq h.unit⁻¹] at hp
    have : h.unit⁻¹ • (p * q) = h.unit⁻¹ • p * q :=
      by
      ext
      simp only [Units.smul_def, coeff_smul, coeff_mul, smul_eq_mul, mul_sum]
      refine' sum_congr rfl fun x _ => _
      rw [← mul_assoc]
    rwa [this, Monic.mul_right_eq_zero_iff] at hp
    exact monic_of_isUnit_leadingCoeff_inv_smul _
  · rintro rfl
    simp
#align polynomial.is_unit_leading_coeff_mul_right_eq_zero_iff Polynomial.isUnit_leadingCoeff_mul_right_eq_zero_iff

theorem isUnit_leadingCoeff_mul_left_eq_zero_iff (h : IsUnit p.leadingCoeff) {q : R[X]} :
    q * p = 0 ↔ q = 0 := by
  constructor
  · intro hp
    replace hp := congr_arg (· * C ↑h.unit⁻¹) hp
    simp only [zero_mul] at hp
    rwa [mul_assoc, Monic.mul_left_eq_zero_iff] at hp
    refine' monic_mul_C_of_leadingCoeff_mul_eq_one _
    simp [Units.mul_inv_eq_iff_eq_mul, IsUnit.unit_spec]
  · rintro rfl
    rw [zero_mul]
#align polynomial.is_unit_leading_coeff_mul_left_eq_zero_iff Polynomial.isUnit_leadingCoeff_mul_left_eq_zero_iff

end NotZeroDivisor

end Polynomial


import mul_subgroup.basic
import tactic

variables {K : Type*} [field K]

lemma ne_zero_left {x y : K} (h : x * y ≠ 0) : x ≠ 0 :=
λ c, h $ by simp [c]

lemma ne_zero_right {x y : K} (h : x * y ≠ 0) : y ≠ 0 :=
λ c, h $ by simp [c]

def mul_subgroup.rigid (T : mul_subgroup K) (x : K) : Prop :=
∀ a b : K, a ∈ T → b ∈ T → x ≠ 0 → a + x * b ∈ T ∨ x⁻¹ * a + b ∈ T

namespace mul_subgroup.rigid

variables {T : mul_subgroup K} {x : K}

lemma mul_left {t : K} (h : T.rigid x) (ht : t ∈ T) :
  T.rigid (t * x) :=
begin
  intros a b ha hb htx,
  cases h a (b * t) ha _ _ with hh hh,
  { left, convert hh using 1, ring },
  { right,
    convert T.mul_mem hh (T.inv_mem ht) using 1,
    field_simp [T.ne_zero_of_mem ht, ne_zero_right htx], ring },
  { exact T.mul_mem hb ht },
  { exact ne_zero_right htx }
end

lemma mul_right {t : K} (h : T.rigid x) (ht : t ∈ T) :
  T.rigid (x * t) :=
by { rw mul_comm, apply mul_left, assumption' }

lemma of_mul_left {t : K} (h : T.rigid (t * x)) (ht : t ∈ T) :
  T.rigid x :=
begin
  convert h.mul_left (T.inv_mem ht),
  field_simp [T.ne_zero_of_mem ht], ring
end

lemma of_mul_right {t : K} (h : T.rigid (x * t)) (ht : t ∈ T) :
  T.rigid x :=
begin
  rw mul_comm at h,
  apply h.of_mul_left ht,
end

lemma mul_left_iff (t : K) (ht : t ∈ T) :
  T.rigid (t * x) ↔ T.rigid x :=
⟨λ h, h.of_mul_left ht, λ h, h.mul_left ht⟩

lemma mul_right_iff (t : K) (ht : t ∈ T) :
  T.rigid (x * t) ↔ T.rigid x :=
by { rw [mul_comm, mul_left_iff t ht] }

end mul_subgroup.rigid


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

! This file was ported from Lean 3 source module data.pi.lex
! leanprover-community/mathlib commit 6623e6af705e97002a9054c1c05a980180276fc1
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.WellFounded
import Mathbin.Algebra.Group.Pi
import Mathbin.Algebra.Order.Group.Defs

/-!
# Lexicographic order on Pi types

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

This file defines the lexicographic order for Pi types. `a` is less than `b` if `a i = b i` for all
`i` up to some point `k`, and `a k < b k`.

## Notation

* `Πₗ i, α i`: Pi type equipped with the lexicographic order. Type synonym of `Π i, α i`.

## See also

Related files are:
* `data.finset.colex`: Colexicographic order on finite sets.
* `data.list.lex`: Lexicographic order on lists.
* `data.sigma.order`: Lexicographic order on `Σₗ i, α i`.
* `data.psigma.order`: Lexicographic order on `Σₗ' i, α i`.
* `data.prod.lex`: Lexicographic order on `α × β`.
-/

variable {ι : Type _} {β : ι → Type _} (r : ι → ι → Prop) (s : ∀ {i}, β i → β i → Prop)

namespace Pi

instance {α : Type _} : ∀ [Inhabited α], Inhabited (Lex α) :=
  id

#print Pi.Lex /-
/-- The lexicographic relation on `Π i : ι, β i`, where `ι` is ordered by `r`,
  and each `β i` is ordered by `s`. -/
protected def Lex (x y : ∀ i, β i) : Prop :=
  ∃ i, (∀ j, r j i → x j = y j) ∧ s (x i) (y i)
#align pi.lex Pi.Lex
-/

-- mathport name: «exprΠₗ , »
notation3"Πₗ "/- This unfortunately results in a type that isn't delta-reduced, so we keep the notation out of the
basic API, just in case -/
(...)", "r:(scoped p => Lex ∀ i, p i) => r

#print Pi.toLex_apply /-
@[simp]
theorem toLex_apply (x : ∀ i, β i) (i : ι) : toLex x i = x i :=
  rfl
#align pi.to_lex_apply Pi.toLex_apply
-/

/- warning: pi.of_lex_apply -> Pi.ofLex_apply is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {β : ι -> Type.{u2}} (x : Lex.{max u1 u2} (forall (i : ι), β i)) (i : ι), Eq.{succ u2} (β i) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (Lex.{max u1 u2} (forall (i : ι), β i)) (forall (i : ι), β i)) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (Lex.{max u1 u2} (forall (i : ι), β i)) (forall (i : ι), β i)) => (Lex.{max u1 u2} (forall (i : ι), β i)) -> (forall (i : ι), β i)) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (Lex.{max u1 u2} (forall (i : ι), β i)) (forall (i : ι), β i)) (ofLex.{max u1 u2} (forall (i : ι), β i)) x i) (x i)
but is expected to have type
  forall {ι : Type.{u2}} {β : ι -> Type.{u1}} (x : Lex.{max u2 u1} (forall (i : ι), β i)) (i : ι), Eq.{succ u1} (β i) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (Lex.{max u2 u1} (forall (i : ι), β i)) (forall (i : ι), β i)) (Lex.{max u2 u1} (forall (i : ι), β i)) (fun (_x : Lex.{max u2 u1} (forall (i : ι), β i)) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Lex.{max u2 u1} (forall (i : ι), β i)) => forall (i : ι), β i) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (Lex.{max u2 u1} (forall (i : ι), β i)) (forall (i : ι), β i)) (ofLex.{max u2 u1} (forall (i : ι), β i)) x i) (x i)
Case conversion may be inaccurate. Consider using '#align pi.of_lex_apply Pi.ofLex_applyₓ'. -/
@[simp]
theorem ofLex_apply (x : Lex (∀ i, β i)) (i : ι) : ofLex x i = x i :=
  rfl
#align pi.of_lex_apply Pi.ofLex_apply

#print Pi.lex_lt_of_lt_of_preorder /-
theorem lex_lt_of_lt_of_preorder [∀ i, Preorder (β i)] {r} (hwf : WellFounded r) {x y : ∀ i, β i}
    (hlt : x < y) : ∃ i, (∀ j, r j i → x j ≤ y j ∧ y j ≤ x j) ∧ x i < y i :=
  let h' := Pi.lt_def.1 hlt
  let ⟨i, hi, hl⟩ := hwf.has_min _ h'.2
  ⟨i, fun j hj => ⟨h'.1 j, Classical.not_not.1 fun h => hl j (lt_of_le_not_le (h'.1 j) h) hj⟩, hi⟩
#align pi.lex_lt_of_lt_of_preorder Pi.lex_lt_of_lt_of_preorder
-/

#print Pi.lex_lt_of_lt /-
theorem lex_lt_of_lt [∀ i, PartialOrder (β i)] {r} (hwf : WellFounded r) {x y : ∀ i, β i}
    (hlt : x < y) : Pi.Lex r (fun i => (· < ·)) x y :=
  by
  simp_rw [Pi.Lex, le_antisymm_iff]
  exact lex_lt_of_lt_of_preorder hwf hlt
#align pi.lex_lt_of_lt Pi.lex_lt_of_lt
-/

#print Pi.isTrichotomous_lex /-
theorem isTrichotomous_lex [∀ i, IsTrichotomous (β i) s] (wf : WellFounded r) :
    IsTrichotomous (∀ i, β i) (Pi.Lex r @s) :=
  {
    trichotomous := fun a b => by
      cases' eq_or_ne a b with hab hab
      · exact Or.inr (Or.inl hab)
      · rw [Function.ne_iff] at hab
        let i := wf.min _ hab
        have hri : ∀ j, r j i → a j = b j := by
          intro j
          rw [← not_imp_not]
          exact fun h' => wf.not_lt_min _ _ h'
        have hne : a i ≠ b i := wf.min_mem _ hab
        cases' trichotomous_of s (a i) (b i) with hi hi
        exacts[Or.inl ⟨i, hri, hi⟩,
          Or.inr <| Or.inr <| ⟨i, fun j hj => (hri j hj).symm, hi.resolve_left hne⟩] }
#align pi.is_trichotomous_lex Pi.isTrichotomous_lex
-/

instance [LT ι] [∀ a, LT (β a)] : LT (Lex (∀ i, β i)) :=
  ⟨Pi.Lex (· < ·) fun _ => (· < ·)⟩

#print Pi.Lex.isStrictOrder /-
instance Lex.isStrictOrder [LinearOrder ι] [∀ a, PartialOrder (β a)] :
    IsStrictOrder (Lex (∀ i, β i)) (· < ·)
    where
  irrefl := fun a ⟨k, hk₁, hk₂⟩ => lt_irrefl (a k) hk₂
  trans := by
    rintro a b c ⟨N₁, lt_N₁, a_lt_b⟩ ⟨N₂, lt_N₂, b_lt_c⟩
    rcases lt_trichotomy N₁ N₂ with (H | rfl | H)
    exacts[⟨N₁, fun j hj => (lt_N₁ _ hj).trans (lt_N₂ _ <| hj.trans H), lt_N₂ _ H ▸ a_lt_b⟩,
      ⟨N₁, fun j hj => (lt_N₁ _ hj).trans (lt_N₂ _ hj), a_lt_b.trans b_lt_c⟩,
      ⟨N₂, fun j hj => (lt_N₁ _ (hj.trans H)).trans (lt_N₂ _ hj), (lt_N₁ _ H).symm ▸ b_lt_c⟩]
#align pi.lex.is_strict_order Pi.Lex.isStrictOrder
-/

instance [LinearOrder ι] [∀ a, PartialOrder (β a)] : PartialOrder (Lex (∀ i, β i)) :=
  partialOrderOfSO (· < ·)

/-- `Πₗ i, α i` is a linear order if the original order is well-founded. -/
noncomputable instance [LinearOrder ι] [IsWellOrder ι (· < ·)] [∀ a, LinearOrder (β a)] :
    LinearOrder (Lex (∀ i, β i)) :=
  @linearOrderOfSTO (Πₗ i, β i) (· < ·)
    { to_isTrichotomous := isTrichotomous_lex _ _ IsWellFounded.wf } (Classical.decRel _)

section PartialOrder

variable [LinearOrder ι] [IsWellOrder ι (· < ·)] [∀ i, PartialOrder (β i)] {x y : ∀ i, β i} {i : ι}
  {a b : β i}

open Function

/- warning: pi.to_lex_monotone -> Pi.toLex_monotone is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {β : ι -> Type.{u2}} [_inst_1 : LinearOrder.{u1} ι] [_inst_2 : IsWellOrder.{u1} ι (LT.lt.{u1} ι (Preorder.toLT.{u1} ι (PartialOrder.toPreorder.{u1} ι (SemilatticeInf.toPartialOrder.{u1} ι (Lattice.toSemilatticeInf.{u1} ι (LinearOrder.toLattice.{u1} ι _inst_1))))))] [_inst_3 : forall (i : ι), PartialOrder.{u2} (β i)], Monotone.{max u1 u2, max u1 u2} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i)) (Pi.preorder.{u1, u2} ι (fun (i : ι) => β i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (β i) (_inst_3 i))) (PartialOrder.toPreorder.{max u1 u2} (Lex.{max u1 u2} (forall (i : ι), β i)) (Pi.Lex.partialOrder.{u1, u2} ι (fun (i : ι) => β i) _inst_1 (fun (a : ι) => _inst_3 a))) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) => (forall (i : ι), β i) -> (Lex.{max u1 u2} (forall (i : ι), β i))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (toLex.{max u1 u2} (forall (i : ι), β i)))
but is expected to have type
  forall {ι : Type.{u2}} {β : ι -> Type.{u1}} [_inst_1 : LinearOrder.{u2} ι] [_inst_2 : IsWellOrder.{u2} ι (fun (x._@.Mathlib.Data.Pi.Lex._hyg.1685 : ι) (x._@.Mathlib.Data.Pi.Lex._hyg.1687 : ι) => LT.lt.{u2} ι (Preorder.toLT.{u2} ι (PartialOrder.toPreorder.{u2} ι (SemilatticeInf.toPartialOrder.{u2} ι (Lattice.toSemilatticeInf.{u2} ι (DistribLattice.toLattice.{u2} ι (instDistribLattice.{u2} ι _inst_1)))))) x._@.Mathlib.Data.Pi.Lex._hyg.1685 x._@.Mathlib.Data.Pi.Lex._hyg.1687)] [_inst_3 : forall (i : ι), PartialOrder.{u1} (β i)], Monotone.{max u2 u1, max u2 u1} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i)) (Pi.preorder.{u2, u1} ι (fun (i : ι) => β i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (β i) (_inst_3 i))) (PartialOrder.toPreorder.{max u2 u1} (Lex.{max u2 u1} (forall (i : ι), β i)) (Pi.instPartialOrderLexForAll.{u2, u1} ι (fun (i : ι) => β i) _inst_1 (fun (a : ι) => _inst_3 a))) (FunLike.coe.{max (succ u2) (succ u1), max (succ u2) (succ u1), max (succ u2) (succ u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (forall (i : ι), β i) (fun (_x : forall (i : ι), β i) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) _x) (Equiv.instFunLikeEquiv.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (toLex.{max u2 u1} (forall (i : ι), β i)))
Case conversion may be inaccurate. Consider using '#align pi.to_lex_monotone Pi.toLex_monotoneₓ'. -/
theorem toLex_monotone : Monotone (@toLex (∀ i, β i)) := fun a b h =>
  or_iff_not_imp_left.2 fun hne =>
    let ⟨i, hi, hl⟩ := IsWellFounded.wf.has_min { i | a i ≠ b i } (Function.ne_iff.1 hne)
    ⟨i, fun j hj => by
      contrapose! hl
      exact ⟨j, hl, hj⟩, (h i).lt_of_ne hi⟩
#align pi.to_lex_monotone Pi.toLex_monotone

/- warning: pi.to_lex_strict_mono -> Pi.toLex_strictMono is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {β : ι -> Type.{u2}} [_inst_1 : LinearOrder.{u1} ι] [_inst_2 : IsWellOrder.{u1} ι (LT.lt.{u1} ι (Preorder.toLT.{u1} ι (PartialOrder.toPreorder.{u1} ι (SemilatticeInf.toPartialOrder.{u1} ι (Lattice.toSemilatticeInf.{u1} ι (LinearOrder.toLattice.{u1} ι _inst_1))))))] [_inst_3 : forall (i : ι), PartialOrder.{u2} (β i)], StrictMono.{max u1 u2, max u1 u2} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i)) (Pi.preorder.{u1, u2} ι (fun (i : ι) => β i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (β i) (_inst_3 i))) (PartialOrder.toPreorder.{max u1 u2} (Lex.{max u1 u2} (forall (i : ι), β i)) (Pi.Lex.partialOrder.{u1, u2} ι (fun (i : ι) => β i) _inst_1 (fun (a : ι) => _inst_3 a))) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) => (forall (i : ι), β i) -> (Lex.{max u1 u2} (forall (i : ι), β i))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (toLex.{max u1 u2} (forall (i : ι), β i)))
but is expected to have type
  forall {ι : Type.{u2}} {β : ι -> Type.{u1}} [_inst_1 : LinearOrder.{u2} ι] [_inst_2 : IsWellOrder.{u2} ι (fun (x._@.Mathlib.Data.Pi.Lex._hyg.1872 : ι) (x._@.Mathlib.Data.Pi.Lex._hyg.1874 : ι) => LT.lt.{u2} ι (Preorder.toLT.{u2} ι (PartialOrder.toPreorder.{u2} ι (SemilatticeInf.toPartialOrder.{u2} ι (Lattice.toSemilatticeInf.{u2} ι (DistribLattice.toLattice.{u2} ι (instDistribLattice.{u2} ι _inst_1)))))) x._@.Mathlib.Data.Pi.Lex._hyg.1872 x._@.Mathlib.Data.Pi.Lex._hyg.1874)] [_inst_3 : forall (i : ι), PartialOrder.{u1} (β i)], StrictMono.{max u2 u1, max u2 u1} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i)) (Pi.preorder.{u2, u1} ι (fun (i : ι) => β i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (β i) (_inst_3 i))) (PartialOrder.toPreorder.{max u2 u1} (Lex.{max u2 u1} (forall (i : ι), β i)) (Pi.instPartialOrderLexForAll.{u2, u1} ι (fun (i : ι) => β i) _inst_1 (fun (a : ι) => _inst_3 a))) (FunLike.coe.{max (succ u2) (succ u1), max (succ u2) (succ u1), max (succ u2) (succ u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (forall (i : ι), β i) (fun (_x : forall (i : ι), β i) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) _x) (Equiv.instFunLikeEquiv.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (toLex.{max u2 u1} (forall (i : ι), β i)))
Case conversion may be inaccurate. Consider using '#align pi.to_lex_strict_mono Pi.toLex_strictMonoₓ'. -/
theorem toLex_strictMono : StrictMono (@toLex (∀ i, β i)) := fun a b h =>
  let ⟨i, hi, hl⟩ := IsWellFounded.wf.has_min { i | a i ≠ b i } (Function.ne_iff.1 h.Ne)
  ⟨i, fun j hj => by
    contrapose! hl
    exact ⟨j, hl, hj⟩, (h.le i).lt_of_ne hi⟩
#align pi.to_lex_strict_mono Pi.toLex_strictMono

/- warning: pi.lt_to_lex_update_self_iff -> Pi.lt_toLex_update_self_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {β : ι -> Type.{u2}} [_inst_1 : LinearOrder.{u1} ι] [_inst_2 : IsWellOrder.{u1} ι (LT.lt.{u1} ι (Preorder.toLT.{u1} ι (PartialOrder.toPreorder.{u1} ι (SemilatticeInf.toPartialOrder.{u1} ι (Lattice.toSemilatticeInf.{u1} ι (LinearOrder.toLattice.{u1} ι _inst_1))))))] [_inst_3 : forall (i : ι), PartialOrder.{u2} (β i)] {x : forall (i : ι), β i} {i : ι} {a : β i}, Iff (LT.lt.{max u1 u2} (Lex.{max u1 u2} (forall (i : ι), β i)) (Pi.Lex.hasLt.{u1, u2} ι (fun (i : ι) => β i) (Preorder.toLT.{u1} ι (PartialOrder.toPreorder.{u1} ι (SemilatticeInf.toPartialOrder.{u1} ι (Lattice.toSemilatticeInf.{u1} ι (LinearOrder.toLattice.{u1} ι _inst_1))))) (fun (a : ι) => Preorder.toLT.{u2} (β a) (PartialOrder.toPreorder.{u2} (β a) (_inst_3 a)))) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) => (forall (i : ι), β i) -> (Lex.{max u1 u2} (forall (i : ι), β i))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (toLex.{max u1 u2} (forall (i : ι), β i)) x) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) => (forall (a : ι), β a) -> (Lex.{max u1 u2} (forall (a : ι), β a))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) (toLex.{max u1 u2} (forall (a : ι), β a)) (Function.update.{succ u1, succ u2} ι (fun (i : ι) => β i) (fun (a : ι) (b : ι) => Eq.decidable.{u1} ι _inst_1 a b) x i a))) (LT.lt.{u2} (β i) (Preorder.toLT.{u2} (β i) (PartialOrder.toPreorder.{u2} (β i) (_inst_3 i))) (x i) a)
but is expected to have type
  forall {ι : Type.{u2}} {β : ι -> Type.{u1}} [_inst_1 : LinearOrder.{u2} ι] [_inst_2 : IsWellOrder.{u2} ι (fun (x._@.Mathlib.Data.Pi.Lex._hyg.2055 : ι) (x._@.Mathlib.Data.Pi.Lex._hyg.2057 : ι) => LT.lt.{u2} ι (Preorder.toLT.{u2} ι (PartialOrder.toPreorder.{u2} ι (SemilatticeInf.toPartialOrder.{u2} ι (Lattice.toSemilatticeInf.{u2} ι (DistribLattice.toLattice.{u2} ι (instDistribLattice.{u2} ι _inst_1)))))) x._@.Mathlib.Data.Pi.Lex._hyg.2055 x._@.Mathlib.Data.Pi.Lex._hyg.2057)] [_inst_3 : forall (i : ι), PartialOrder.{u1} (β i)] {x : forall (i : ι), β i} {i : ι} {a : β i}, Iff (LT.lt.{max u2 u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) x) (Pi.instLTLexForAll.{u2, u1} ι (fun (i : ι) => β i) (Preorder.toLT.{u2} ι (PartialOrder.toPreorder.{u2} ι (SemilatticeInf.toPartialOrder.{u2} ι (Lattice.toSemilatticeInf.{u2} ι (DistribLattice.toLattice.{u2} ι (instDistribLattice.{u2} ι _inst_1)))))) (fun (a : ι) => Preorder.toLT.{u1} (β a) (PartialOrder.toPreorder.{u1} (β a) (_inst_3 a)))) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (forall (i : ι), β i) (fun (_x : forall (i : ι), β i) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (toLex.{max u2 u1} (forall (i : ι), β i)) x) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (a : ι), β a) (Lex.{max u2 u1} (forall (a : ι), β a))) (forall (a : ι), β a) (fun (_x : forall (a : ι), β a) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (a : ι), β a) => Lex.{max u2 u1} (forall (a : ι), β a)) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (forall (a : ι), β a) (Lex.{max u2 u1} (forall (a : ι), β a))) (toLex.{max u2 u1} (forall (a : ι), β a)) (Function.update.{succ u2, succ u1} ι (fun (i : ι) => β i) (fun (a : ι) (b : ι) => instDecidableEq.{u2} ι _inst_1 a b) x i a))) (LT.lt.{u1} (β i) (Preorder.toLT.{u1} (β i) (PartialOrder.toPreorder.{u1} (β i) (_inst_3 i))) (x i) a)
Case conversion may be inaccurate. Consider using '#align pi.lt_to_lex_update_self_iff Pi.lt_toLex_update_self_iffₓ'. -/
@[simp]
theorem lt_toLex_update_self_iff : toLex x < toLex (update x i a) ↔ x i < a :=
  by
  refine' ⟨_, fun h => to_lex_strict_mono <| lt_update_self_iff.2 h⟩
  rintro ⟨j, hj, h⟩
  dsimp at h
  obtain rfl : j = i := by
    by_contra H
    rw [update_noteq H] at h
    exact h.false
  · rwa [update_same] at h
#align pi.lt_to_lex_update_self_iff Pi.lt_toLex_update_self_iff

/- warning: pi.to_lex_update_lt_self_iff -> Pi.toLex_update_lt_self_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {β : ι -> Type.{u2}} [_inst_1 : LinearOrder.{u1} ι] [_inst_2 : IsWellOrder.{u1} ι (LT.lt.{u1} ι (Preorder.toLT.{u1} ι (PartialOrder.toPreorder.{u1} ι (SemilatticeInf.toPartialOrder.{u1} ι (Lattice.toSemilatticeInf.{u1} ι (LinearOrder.toLattice.{u1} ι _inst_1))))))] [_inst_3 : forall (i : ι), PartialOrder.{u2} (β i)] {x : forall (i : ι), β i} {i : ι} {a : β i}, Iff (LT.lt.{max u1 u2} (Lex.{max u1 u2} (forall (a : ι), β a)) (Pi.Lex.hasLt.{u1, u2} ι (fun (a : ι) => β a) (Preorder.toLT.{u1} ι (PartialOrder.toPreorder.{u1} ι (SemilatticeInf.toPartialOrder.{u1} ι (Lattice.toSemilatticeInf.{u1} ι (LinearOrder.toLattice.{u1} ι _inst_1))))) (fun (a : ι) => Preorder.toLT.{u2} (β a) (PartialOrder.toPreorder.{u2} (β a) (_inst_3 a)))) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) => (forall (a : ι), β a) -> (Lex.{max u1 u2} (forall (a : ι), β a))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) (toLex.{max u1 u2} (forall (a : ι), β a)) (Function.update.{succ u1, succ u2} ι (fun (i : ι) => β i) (fun (a : ι) (b : ι) => Eq.decidable.{u1} ι _inst_1 a b) x i a)) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) => (forall (i : ι), β i) -> (Lex.{max u1 u2} (forall (i : ι), β i))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (toLex.{max u1 u2} (forall (i : ι), β i)) x)) (LT.lt.{u2} (β i) (Preorder.toLT.{u2} (β i) (PartialOrder.toPreorder.{u2} (β i) (_inst_3 i))) a (x i))
but is expected to have type
  forall {ι : Type.{u2}} {β : ι -> Type.{u1}} [_inst_1 : LinearOrder.{u2} ι] [_inst_2 : IsWellOrder.{u2} ι (fun (x._@.Mathlib.Data.Pi.Lex._hyg.2353 : ι) (x._@.Mathlib.Data.Pi.Lex._hyg.2355 : ι) => LT.lt.{u2} ι (Preorder.toLT.{u2} ι (PartialOrder.toPreorder.{u2} ι (SemilatticeInf.toPartialOrder.{u2} ι (Lattice.toSemilatticeInf.{u2} ι (DistribLattice.toLattice.{u2} ι (instDistribLattice.{u2} ι _inst_1)))))) x._@.Mathlib.Data.Pi.Lex._hyg.2353 x._@.Mathlib.Data.Pi.Lex._hyg.2355)] [_inst_3 : forall (i : ι), PartialOrder.{u1} (β i)] {x : forall (i : ι), β i} {i : ι} {a : β i}, Iff (LT.lt.{max u2 u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (a : ι), β a) => Lex.{max u2 u1} (forall (a : ι), β a)) (Function.update.{succ u2, succ u1} ι (fun (a : ι) => β a) (fun (a : ι) (b : ι) => instDecidableEq.{u2} ι _inst_1 a b) x i a)) (Pi.instLTLexForAll.{u2, u1} ι (fun (a : ι) => β a) (Preorder.toLT.{u2} ι (PartialOrder.toPreorder.{u2} ι (SemilatticeInf.toPartialOrder.{u2} ι (Lattice.toSemilatticeInf.{u2} ι (DistribLattice.toLattice.{u2} ι (instDistribLattice.{u2} ι _inst_1)))))) (fun (a : ι) => Preorder.toLT.{u1} (β a) (PartialOrder.toPreorder.{u1} (β a) (_inst_3 a)))) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (a : ι), β a) (Lex.{max u2 u1} (forall (a : ι), β a))) (forall (a : ι), β a) (fun (_x : forall (a : ι), β a) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (a : ι), β a) => Lex.{max u2 u1} (forall (a : ι), β a)) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (forall (a : ι), β a) (Lex.{max u2 u1} (forall (a : ι), β a))) (toLex.{max u2 u1} (forall (a : ι), β a)) (Function.update.{succ u2, succ u1} ι (fun (i : ι) => β i) (fun (a : ι) (b : ι) => instDecidableEq.{u2} ι _inst_1 a b) x i a)) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (forall (i : ι), β i) (fun (_x : forall (i : ι), β i) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (toLex.{max u2 u1} (forall (i : ι), β i)) x)) (LT.lt.{u1} (β i) (Preorder.toLT.{u1} (β i) (PartialOrder.toPreorder.{u1} (β i) (_inst_3 i))) a (x i))
Case conversion may be inaccurate. Consider using '#align pi.to_lex_update_lt_self_iff Pi.toLex_update_lt_self_iffₓ'. -/
@[simp]
theorem toLex_update_lt_self_iff : toLex (update x i a) < toLex x ↔ a < x i :=
  by
  refine' ⟨_, fun h => to_lex_strict_mono <| update_lt_self_iff.2 h⟩
  rintro ⟨j, hj, h⟩
  dsimp at h
  obtain rfl : j = i := by
    by_contra H
    rw [update_noteq H] at h
    exact h.false
  · rwa [update_same] at h
#align pi.to_lex_update_lt_self_iff Pi.toLex_update_lt_self_iff

/- warning: pi.le_to_lex_update_self_iff -> Pi.le_toLex_update_self_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {β : ι -> Type.{u2}} [_inst_1 : LinearOrder.{u1} ι] [_inst_2 : IsWellOrder.{u1} ι (LT.lt.{u1} ι (Preorder.toLT.{u1} ι (PartialOrder.toPreorder.{u1} ι (SemilatticeInf.toPartialOrder.{u1} ι (Lattice.toSemilatticeInf.{u1} ι (LinearOrder.toLattice.{u1} ι _inst_1))))))] [_inst_3 : forall (i : ι), PartialOrder.{u2} (β i)] {x : forall (i : ι), β i} {i : ι} {a : β i}, Iff (LE.le.{max u1 u2} (Lex.{max u1 u2} (forall (i : ι), β i)) (Preorder.toLE.{max u1 u2} (Lex.{max u1 u2} (forall (i : ι), β i)) (PartialOrder.toPreorder.{max u1 u2} (Lex.{max u1 u2} (forall (i : ι), β i)) (Pi.Lex.partialOrder.{u1, u2} ι (fun (i : ι) => β i) _inst_1 (fun (a : ι) => _inst_3 a)))) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) => (forall (i : ι), β i) -> (Lex.{max u1 u2} (forall (i : ι), β i))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (toLex.{max u1 u2} (forall (i : ι), β i)) x) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) => (forall (a : ι), β a) -> (Lex.{max u1 u2} (forall (a : ι), β a))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) (toLex.{max u1 u2} (forall (a : ι), β a)) (Function.update.{succ u1, succ u2} ι (fun (i : ι) => β i) (fun (a : ι) (b : ι) => Eq.decidable.{u1} ι _inst_1 a b) x i a))) (LE.le.{u2} (β i) (Preorder.toLE.{u2} (β i) (PartialOrder.toPreorder.{u2} (β i) (_inst_3 i))) (x i) a)
but is expected to have type
  forall {ι : Type.{u2}} {β : ι -> Type.{u1}} [_inst_1 : LinearOrder.{u2} ι] [_inst_2 : IsWellOrder.{u2} ι (fun (x._@.Mathlib.Data.Pi.Lex._hyg.2651 : ι) (x._@.Mathlib.Data.Pi.Lex._hyg.2653 : ι) => LT.lt.{u2} ι (Preorder.toLT.{u2} ι (PartialOrder.toPreorder.{u2} ι (SemilatticeInf.toPartialOrder.{u2} ι (Lattice.toSemilatticeInf.{u2} ι (DistribLattice.toLattice.{u2} ι (instDistribLattice.{u2} ι _inst_1)))))) x._@.Mathlib.Data.Pi.Lex._hyg.2651 x._@.Mathlib.Data.Pi.Lex._hyg.2653)] [_inst_3 : forall (i : ι), PartialOrder.{u1} (β i)] {x : forall (i : ι), β i} {i : ι} {a : β i}, Iff (LE.le.{max u2 u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) x) (Preorder.toLE.{max u2 u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) x) (PartialOrder.toPreorder.{max u2 u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) x) (Pi.instPartialOrderLexForAll.{u2, u1} ι (fun (i : ι) => β i) _inst_1 (fun (a : ι) => _inst_3 a)))) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (forall (i : ι), β i) (fun (_x : forall (i : ι), β i) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (toLex.{max u2 u1} (forall (i : ι), β i)) x) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (a : ι), β a) (Lex.{max u2 u1} (forall (a : ι), β a))) (forall (a : ι), β a) (fun (_x : forall (a : ι), β a) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (a : ι), β a) => Lex.{max u2 u1} (forall (a : ι), β a)) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (forall (a : ι), β a) (Lex.{max u2 u1} (forall (a : ι), β a))) (toLex.{max u2 u1} (forall (a : ι), β a)) (Function.update.{succ u2, succ u1} ι (fun (i : ι) => β i) (fun (a : ι) (b : ι) => instDecidableEq.{u2} ι _inst_1 a b) x i a))) (LE.le.{u1} (β i) (Preorder.toLE.{u1} (β i) (PartialOrder.toPreorder.{u1} (β i) (_inst_3 i))) (x i) a)
Case conversion may be inaccurate. Consider using '#align pi.le_to_lex_update_self_iff Pi.le_toLex_update_self_iffₓ'. -/
@[simp]
theorem le_toLex_update_self_iff : toLex x ≤ toLex (update x i a) ↔ x i ≤ a := by
  simp_rw [le_iff_lt_or_eq, lt_to_lex_update_self_iff, toLex_inj, eq_update_self_iff]
#align pi.le_to_lex_update_self_iff Pi.le_toLex_update_self_iff

/- warning: pi.to_lex_update_le_self_iff -> Pi.toLex_update_le_self_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {β : ι -> Type.{u2}} [_inst_1 : LinearOrder.{u1} ι] [_inst_2 : IsWellOrder.{u1} ι (LT.lt.{u1} ι (Preorder.toLT.{u1} ι (PartialOrder.toPreorder.{u1} ι (SemilatticeInf.toPartialOrder.{u1} ι (Lattice.toSemilatticeInf.{u1} ι (LinearOrder.toLattice.{u1} ι _inst_1))))))] [_inst_3 : forall (i : ι), PartialOrder.{u2} (β i)] {x : forall (i : ι), β i} {i : ι} {a : β i}, Iff (LE.le.{max u1 u2} (Lex.{max u1 u2} (forall (a : ι), β a)) (Preorder.toLE.{max u1 u2} (Lex.{max u1 u2} (forall (a : ι), β a)) (PartialOrder.toPreorder.{max u1 u2} (Lex.{max u1 u2} (forall (a : ι), β a)) (Pi.Lex.partialOrder.{u1, u2} ι (fun (a : ι) => β a) _inst_1 (fun (a : ι) => _inst_3 a)))) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) => (forall (a : ι), β a) -> (Lex.{max u1 u2} (forall (a : ι), β a))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (a : ι), β a) (Lex.{max u1 u2} (forall (a : ι), β a))) (toLex.{max u1 u2} (forall (a : ι), β a)) (Function.update.{succ u1, succ u2} ι (fun (i : ι) => β i) (fun (a : ι) (b : ι) => Eq.decidable.{u1} ι _inst_1 a b) x i a)) (coeFn.{succ (max u1 u2), succ (max u1 u2)} (Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (fun (_x : Equiv.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) => (forall (i : ι), β i) -> (Lex.{max u1 u2} (forall (i : ι), β i))) (Equiv.hasCoeToFun.{succ (max u1 u2), succ (max u1 u2)} (forall (i : ι), β i) (Lex.{max u1 u2} (forall (i : ι), β i))) (toLex.{max u1 u2} (forall (i : ι), β i)) x)) (LE.le.{u2} (β i) (Preorder.toLE.{u2} (β i) (PartialOrder.toPreorder.{u2} (β i) (_inst_3 i))) a (x i))
but is expected to have type
  forall {ι : Type.{u2}} {β : ι -> Type.{u1}} [_inst_1 : LinearOrder.{u2} ι] [_inst_2 : IsWellOrder.{u2} ι (fun (x._@.Mathlib.Data.Pi.Lex._hyg.2749 : ι) (x._@.Mathlib.Data.Pi.Lex._hyg.2751 : ι) => LT.lt.{u2} ι (Preorder.toLT.{u2} ι (PartialOrder.toPreorder.{u2} ι (SemilatticeInf.toPartialOrder.{u2} ι (Lattice.toSemilatticeInf.{u2} ι (DistribLattice.toLattice.{u2} ι (instDistribLattice.{u2} ι _inst_1)))))) x._@.Mathlib.Data.Pi.Lex._hyg.2749 x._@.Mathlib.Data.Pi.Lex._hyg.2751)] [_inst_3 : forall (i : ι), PartialOrder.{u1} (β i)] {x : forall (i : ι), β i} {i : ι} {a : β i}, Iff (LE.le.{max u2 u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (a : ι), β a) => Lex.{max u2 u1} (forall (a : ι), β a)) (Function.update.{succ u2, succ u1} ι (fun (a : ι) => β a) (fun (a : ι) (b : ι) => instDecidableEq.{u2} ι _inst_1 a b) x i a)) (Preorder.toLE.{max u2 u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (a : ι), β a) => Lex.{max u2 u1} (forall (a : ι), β a)) (Function.update.{succ u2, succ u1} ι (fun (a : ι) => β a) (fun (a : ι) (b : ι) => instDecidableEq.{u2} ι _inst_1 a b) x i a)) (PartialOrder.toPreorder.{max u2 u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (a : ι), β a) => Lex.{max u2 u1} (forall (a : ι), β a)) (Function.update.{succ u2, succ u1} ι (fun (a : ι) => β a) (fun (a : ι) (b : ι) => instDecidableEq.{u2} ι _inst_1 a b) x i a)) (Pi.instPartialOrderLexForAll.{u2, u1} ι (fun (a : ι) => β a) _inst_1 (fun (a : ι) => _inst_3 a)))) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (a : ι), β a) (Lex.{max u2 u1} (forall (a : ι), β a))) (forall (a : ι), β a) (fun (_x : forall (a : ι), β a) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (a : ι), β a) => Lex.{max u2 u1} (forall (a : ι), β a)) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (forall (a : ι), β a) (Lex.{max u2 u1} (forall (a : ι), β a))) (toLex.{max u2 u1} (forall (a : ι), β a)) (Function.update.{succ u2, succ u1} ι (fun (i : ι) => β i) (fun (a : ι) (b : ι) => instDecidableEq.{u2} ι _inst_1 a b) x i a)) (FunLike.coe.{succ (max u2 u1), succ (max u2 u1), succ (max u2 u1)} (Equiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (forall (i : ι), β i) (fun (_x : forall (i : ι), β i) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : forall (i : ι), β i) => Lex.{max u2 u1} (forall (i : ι), β i)) _x) (Equiv.instFunLikeEquiv.{succ (max u2 u1), succ (max u2 u1)} (forall (i : ι), β i) (Lex.{max u2 u1} (forall (i : ι), β i))) (toLex.{max u2 u1} (forall (i : ι), β i)) x)) (LE.le.{u1} (β i) (Preorder.toLE.{u1} (β i) (PartialOrder.toPreorder.{u1} (β i) (_inst_3 i))) a (x i))
Case conversion may be inaccurate. Consider using '#align pi.to_lex_update_le_self_iff Pi.toLex_update_le_self_iffₓ'. -/
@[simp]
theorem toLex_update_le_self_iff : toLex (update x i a) ≤ toLex x ↔ a ≤ x i := by
  simp_rw [le_iff_lt_or_eq, to_lex_update_lt_self_iff, toLex_inj, update_eq_self_iff]
#align pi.to_lex_update_le_self_iff Pi.toLex_update_le_self_iff

end PartialOrder

instance [LinearOrder ι] [IsWellOrder ι (· < ·)] [∀ a, PartialOrder (β a)] [∀ a, OrderBot (β a)] :
    OrderBot (Lex (∀ a, β a)) where
  bot := toLex ⊥
  bot_le f := toLex_monotone bot_le

instance [LinearOrder ι] [IsWellOrder ι (· < ·)] [∀ a, PartialOrder (β a)] [∀ a, OrderTop (β a)] :
    OrderTop (Lex (∀ a, β a)) where
  top := toLex ⊤
  le_top f := toLex_monotone le_top

instance [LinearOrder ι] [IsWellOrder ι (· < ·)] [∀ a, PartialOrder (β a)]
    [∀ a, BoundedOrder (β a)] : BoundedOrder (Lex (∀ a, β a)) :=
  { Pi.Lex.orderBot, Pi.Lex.orderTop with }

instance [Preorder ι] [∀ i, LT (β i)] [∀ i, DenselyOrdered (β i)] :
    DenselyOrdered (Lex (∀ i, β i)) :=
  ⟨by
    rintro _ _ ⟨i, h, hi⟩
    obtain ⟨a, ha₁, ha₂⟩ := exists_between hi
    classical
      refine' ⟨a₂.update _ a, ⟨i, fun j hj => _, _⟩, i, fun j hj => _, _⟩
      rw [h j hj]
      iterate 2 · rw [a₂.update_noteq hj.ne a]; · rwa [a₂.update_same i a]⟩

/- warning: pi.lex.no_max_order' -> Pi.Lex.noMaxOrder' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {β : ι -> Type.{u2}} [_inst_1 : Preorder.{u1} ι] [_inst_2 : forall (i : ι), LT.{u2} (β i)] (i : ι) [_inst_3 : NoMaxOrder.{u2} (β i) (_inst_2 i)], NoMaxOrder.{max u1 u2} (Lex.{max u1 u2} (forall (i : ι), β i)) (Pi.Lex.hasLt.{u1, u2} ι (fun (i : ι) => β i) (Preorder.toLT.{u1} ι _inst_1) (fun (a : ι) => _inst_2 a))
but is expected to have type
  forall {ι : Type.{u2}} {β : ι -> Type.{u1}} [_inst_1 : Preorder.{u2} ι] [_inst_2 : forall (i : ι), LT.{u1} (β i)] (i : ι) [_inst_3 : NoMaxOrder.{u1} (β i) (_inst_2 i)], NoMaxOrder.{max u2 u1} (Lex.{max u2 u1} (forall (i : ι), β i)) (Pi.instLTLexForAll.{u2, u1} ι (fun (i : ι) => β i) (Preorder.toLT.{u2} ι _inst_1) (fun (a : ι) => _inst_2 a))
Case conversion may be inaccurate. Consider using '#align pi.lex.no_max_order' Pi.Lex.noMaxOrder'ₓ'. -/
theorem Lex.noMaxOrder' [Preorder ι] [∀ i, LT (β i)] (i : ι) [NoMaxOrder (β i)] :
    NoMaxOrder (Lex (∀ i, β i)) :=
  ⟨fun a => by
    classical
      obtain ⟨b, hb⟩ := exists_gt (a i)
      exact
        ⟨a.update i b, i, fun j hj => (a.update_noteq hj.Ne b).symm, by rwa [a.update_same i b]⟩⟩
#align pi.lex.no_max_order' Pi.Lex.noMaxOrder'

instance [LinearOrder ι] [IsWellOrder ι (· < ·)] [Nonempty ι] [∀ i, PartialOrder (β i)]
    [∀ i, NoMaxOrder (β i)] : NoMaxOrder (Lex (∀ i, β i)) :=
  ⟨fun a =>
    let ⟨b, hb⟩ := exists_gt (ofLex a)
    ⟨_, toLex_strictMono hb⟩⟩

instance [LinearOrder ι] [IsWellOrder ι (· < ·)] [Nonempty ι] [∀ i, PartialOrder (β i)]
    [∀ i, NoMinOrder (β i)] : NoMinOrder (Lex (∀ i, β i)) :=
  ⟨fun a =>
    let ⟨b, hb⟩ := exists_lt (ofLex a)
    ⟨_, toLex_strictMono hb⟩⟩

#print Pi.Lex.orderedCommGroup /-
--we might want the analog of `pi.ordered_cancel_comm_monoid` as well in the future
@[to_additive]
instance Lex.orderedCommGroup [LinearOrder ι] [∀ a, OrderedCommGroup (β a)] :
    OrderedCommGroup (Lex (∀ i, β i)) :=
  { Pi.Lex.partialOrder, Pi.commGroup with
    mul_le_mul_left := fun x y hxy z =>
      hxy.elim (fun hxyz => hxyz ▸ le_rfl) fun ⟨i, hi⟩ =>
        Or.inr
          ⟨i, fun j hji => show z j * x j = z j * y j by rw [hi.1 j hji], mul_lt_mul_left' hi.2 _⟩ }
#align pi.lex.ordered_comm_group Pi.Lex.orderedCommGroup
#align pi.lex.ordered_add_comm_group Pi.Lex.orderedAddCommGroup
-/

#print Pi.lex_desc /-
/-- If we swap two strictly decreasing values in a function, then the result is lexicographically
smaller than the original function. -/
theorem lex_desc {α} [Preorder ι] [DecidableEq ι] [Preorder α] {f : ι → α} {i j : ι} (h₁ : i < j)
    (h₂ : f j < f i) : toLex (f ∘ Equiv.swap i j) < toLex f :=
  ⟨i, fun k hik => congr_arg f (Equiv.swap_apply_of_ne_of_ne hik.Ne (hik.trans h₁).Ne), by
    simpa only [Pi.toLex_apply, Function.comp_apply, Equiv.swap_apply_left] using h₂⟩
#align pi.lex_desc Pi.lex_desc
-/

end Pi


class HasMulComm (α : Type u) [Mul α] : Prop where
    mulComm : {a b : α} → a * b = b * a

class A (α : Type u) extends Mul α
attribute [instance] A.mk

class B (α : Type u) extends A α, HasMulComm α
attribute [instance] B.mk

example [Mul α] : A α := inferInstance
example [Mul α] [HasMulComm α] : A α := inferInstance
example [B α] : A α := inferInstance

example [A α] [HasMulComm α] : B α := inferInstance
example [Mul α] [HasMulComm α] : B α := inferInstance
example [Mul α] [HasMulComm α] : B α := B.mk


import challenge_notations
import challenge_prerequisites

/-!
# Liquid Tensor Experiment

## The main challenge

The main challenge of the liquid tensor experiment is
a formalisation of the first theorem in Peter Scholze's blogpost
https://xenaproject.wordpress.com/2020/12/05/liquid-tensor-experiment/

Theorem 1.1 (Clausen--Scholze)
Let `0 < p' < p ≤ 1` be real numbers, let `S` be a profinite set, and let `V` be a `p`-Banach space.
Let `ℳ p' S` be the space of `p'`-measures on `S`. Then
$$ Ext^i (ℳ p' S, V) = 0 $$
for `i ≥ 1`.

-/

noncomputable theory

open_locale liquid_tensor_experiment nnreal zero_object
open liquid_tensor_experiment category_theory category_theory.limits

variables (p' p : ℝ≥0) [fact (0 < p')] [fact (p' < p)] [fact (p ≤ 1)]

theorem liquid_tensor_experiment (S : Profinite.{0}) (V : pBanach.{0} p) :
  ∀ i > 0, Ext i (ℳ_{p'} S) V ≅ 0 :=
begin
  intros i hi,
  apply is_zero.iso_zero,
  revert i,
  haveI : fact (0 < (p:ℝ)) := ⟨lt_trans (fact.out _ : 0 < p') (fact.out _)⟩,
  haveI : fact (p' < 1) := ⟨lt_of_lt_of_le (fact.out _ : p' < p) (fact.out _)⟩,
  erw is_zero_iff_epi_and_is_iso _ _ (V : Condensed.{0 1 2} Ab) (laurent_measures.short_exact p' S),
  let := pBanach.choose_seminormed_add_comm_group V,
  let := pBanach.choose_normed_with_aut V 2⁻¹,
  haveI : fact (0 < (2⁻¹ : ℝ≥0) ^ (p : ℝ)) := r_pos',
  convert laurent_measures.epi_and_is_iso p' p S ⟨V⟩ _ using 1,
  intro v,
  rw [pBanach.choose_normed_with_aut_T_inv, inv_inv, two_smul, two_nsmul],
end


import field_theory.splitting_field
import number_theory.bernoulli
import number_theory.class_number.finite
import number_theory.class_number.admissible_abs
import number_theory.cyclotomic.cycl_rat
import number_theory.cyclotomic.rat

/-!
# Regular primes

## Main definitions

* `is_regular_number`: a natural number `n` is regular if `n` is coprime with the cardinal of the
  class group.

-/

noncomputable theory
open nat polynomial

open number_field
open_locale classical number_field

section safe_instances

/- The idea of `open_locale cyclotomic` is that it provides some of these instances when needed,
but sadly its implementation is so unsafe that using it here creates a lot of diamonds.
We instead put some safe specialised instances here, and we can maybe look at generalising them
later, when this is needed. Most results from here on genuinely only work for ℚ, so this is
very fine for the moment. -/

instance safe {p : ℕ+} := is_cyclotomic_extension.number_field {p} ℚ $ cyclotomic_field p ℚ
instance safe' {p : ℕ+} := is_cyclotomic_extension.finite_dimensional {p} ℚ $ cyclotomic_field p ℚ

instance cyclotomic_field.class_group_finite {p : ℕ+} :
  fintype (class_group $ 𝓞 (cyclotomic_field p ℚ)) :=
class_group.fintype_of_admissible_of_finite ℚ (cyclotomic_field p ℚ)
  absolute_value.abs_is_admissible

end safe_instances

variables (n p : ℕ) [fact p.prime]

instance {p : ℕ} [hp : fact p.prime] : fact (0 < p) := ⟨hp.out.pos⟩

-- note that this definition can be annoying to work with whilst #14984 isn't merged.
/-- A natural number `n` is regular if `n` is coprime with the cardinal of the class group -/
def is_regular_number [hn : fact (0 < n)]  : Prop :=
n.coprime $ fintype.card $ class_group (𝓞 $ cyclotomic_field ⟨n, hn.out⟩ ℚ)

/-- The definition of regular primes. -/
def is_regular_prime : Prop := is_regular_number p

/-- A prime number is Bernoulli regular if it does not divide the numerator of any of
the first `p - 3` (non-zero) Bernoulli numbers-/
def is_Bernoulli_regular : Prop :=
∀ i ∈ finset.range((p - 3) / 2), ((bernoulli 2 * i).num : zmod p) ≠ 0

/--A prime is super regular if its regular and Bernoulli regular.-/
def is_super_regular : Prop :=
 is_regular_number p ∧ is_Bernoulli_regular p

section two_regular

variables (A K : Type*) [comm_ring A] [is_domain A] [field K] [algebra A K] [is_fraction_ring A K]

variables (L : Type*) [field L] [algebra K L]

/-- The second cyclotomic field is equivalent to the base field. -/
def cyclotomic_field_two_equiv [is_cyclotomic_extension {2} K L] : L ≃ₐ[K] K :=
begin
  suffices : is_splitting_field K K (cyclotomic 2 K),
  { letI : is_splitting_field K L (cyclotomic 2 K) :=
      is_cyclotomic_extension.splitting_field_cyclotomic 2 K L,
    exact (is_splitting_field.alg_equiv L (cyclotomic 2 K)).trans
      (is_splitting_field.alg_equiv K $ cyclotomic 2 K).symm },
  exact ⟨by simpa using @splits_X_sub_C _ _ _ _ (ring_hom.id K) (-1), by simp⟩,
end

instance (L : Type*) [field L] [char_zero L] [is_cyclotomic_extension {2} ℚ L] :
  is_principal_ideal_ring (𝓞 L) :=
begin
  haveI : is_integral_closure ℤ ℤ L :=
  { algebra_map_injective := (algebra_map ℤ L).injective_int,
    is_integral_iff := λ x,
    begin
      let f := (cyclotomic_field_two_equiv ℚ L),
      refine ⟨λ hx, ⟨is_integral_closure.mk' ℤ (f x) (map_is_integral_int f hx), f.injective _⟩, _⟩,
      { convert is_integral_closure.algebra_map_mk' ℤ (f x) (map_is_integral_int f hx),
        simp },
      { rintro ⟨y, hy⟩,
        simpa [← hy] using is_integral_algebra_map }
    end },
  let F : 𝓞 L ≃+* ℤ := number_field.ring_of_integers.equiv _,
  exact is_principal_ideal_ring.of_surjective F.symm.to_ring_hom F.symm.surjective,
end

local attribute [-instance] cyclotomic_field.algebra

lemma is_regular_number_two : is_regular_number 2 :=
begin
  haveI : is_cyclotomic_extension {2} ℚ (cyclotomic_field 2 ℚ),
  { convert cyclotomic_field.is_cyclotomic_extension 2 _,
    { exact subsingleton.elim _ _ },
    { refine ⟨λ h, by simpa using h⟩ } },
  rw is_regular_number,
  convert coprime_one_right _,
  dsimp,
  rw card_class_group_eq_one_iff,
  apply_instance,
end

end two_regular


/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura

! This file was ported from Lean 3 source module data.rbtree.min_max
! leanprover-community/mathlib commit 8f6fd1b69096c6a587f745d354306c0d46396915
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Rbtree.Basic

universe u

namespace Rbnode

variable {α : Type u} {lt : α → α → Prop}

theorem mem_of_min_eq (lt : α → α → Prop) [IsIrrefl α lt] {a : α} {t : Rbnode α} :
    t.min = some a → Mem lt a t := by
  induction t
  · intros
    contradiction
  all_goals
    cases t_lchild <;> simp [Rbnode.min] <;> intro h
    · subst t_val
      simp [mem, irrefl_of lt a]
    all_goals rw [mem]; simp [t_ih_lchild h]
#align rbnode.mem_of_min_eq Rbnode.mem_of_min_eq

theorem mem_of_max_eq (lt : α → α → Prop) [IsIrrefl α lt] {a : α} {t : Rbnode α} :
    t.max = some a → Mem lt a t := by
  induction t
  · intros
    contradiction
  all_goals
    cases t_rchild <;> simp [Rbnode.max] <;> intro h
    · subst t_val
      simp [mem, irrefl_of lt a]
    all_goals rw [mem]; simp [t_ih_rchild h]
#align rbnode.mem_of_max_eq Rbnode.mem_of_max_eq

variable [IsStrictWeakOrder α lt]

theorem eq_leaf_of_min_eq_none {t : Rbnode α} : t.min = none → t = leaf :=
  by
  induction t
  · intros
    rfl
  all_goals
    cases t_lchild <;> simp [Rbnode.min, false_imp_iff] <;> intro h
    all_goals have := t_ih_lchild h; contradiction
#align rbnode.eq_leaf_of_min_eq_none Rbnode.eq_leaf_of_min_eq_none

theorem eq_leaf_of_max_eq_none {t : Rbnode α} : t.max = none → t = leaf :=
  by
  induction t
  · intros
    rfl
  all_goals
    cases t_rchild <;> simp [Rbnode.max, false_imp_iff] <;> intro h
    all_goals have := t_ih_rchild h; contradiction
#align rbnode.eq_leaf_of_max_eq_none Rbnode.eq_leaf_of_max_eq_none

theorem min_is_minimal {a : α} {t : Rbnode α} :
    ∀ {lo hi}, IsSearchable lt t lo hi → t.min = some a → ∀ {b}, Mem lt b t → a ≈[lt]b ∨ lt a b :=
  by
  classical
    induction t
    · simp [StrictWeakOrder.Equiv]
      intro _ _ hs hmin b
      contradiction
    all_goals
      cases t_lchild <;> intro lo hi hs hmin b hmem
      · simp [Rbnode.min] at hmin
        subst t_val
        simp [mem] at hmem
        cases' hmem with heqv hmem
        · left
          exact heqv.swap
        · have := lt_of_mem_right hs (by constructor) hmem
          right
          assumption
      all_goals
        have hs' := hs
        cases hs; simp [Rbnode.min] at hmin
        rw [mem] at hmem; cases_type*or.1
        · exact t_ih_lchild hs_hs₁ hmin hmem
        · have hmm := mem_of_min_eq lt hmin
          have a_lt_val := lt_of_mem_left hs' (by constructor) hmm
          have a_lt_b := lt_of_lt_of_incomp a_lt_val hmem.swap
          right
          assumption
        · have hmm := mem_of_min_eq lt hmin
          have a_lt_b := lt_of_mem_left_right hs' (by constructor) hmm hmem
          right
          assumption
#align rbnode.min_is_minimal Rbnode.min_is_minimal

theorem max_is_maximal {a : α} {t : Rbnode α} :
    ∀ {lo hi}, IsSearchable lt t lo hi → t.max = some a → ∀ {b}, Mem lt b t → a ≈[lt]b ∨ lt b a :=
  by
  classical
    induction t
    · simp [StrictWeakOrder.Equiv]
      intro _ _ hs hmax b
      contradiction
    all_goals
      cases t_rchild <;> intro lo hi hs hmax b hmem
      · simp [Rbnode.max] at hmax
        subst t_val
        simp [mem] at hmem
        cases' hmem with hmem heqv
        · have := lt_of_mem_left hs (by constructor) hmem
          right
          assumption
        · left
          exact heqv.swap
      all_goals
        have hs' := hs
        cases hs; simp [Rbnode.max] at hmax
        rw [mem] at hmem; cases_type*or.1
        · have hmm := mem_of_max_eq lt hmax
          have a_lt_b := lt_of_mem_left_right hs' (by constructor) hmem hmm
          right
          assumption
        · have hmm := mem_of_max_eq lt hmax
          have val_lt_a := lt_of_mem_right hs' (by constructor) hmm
          have a_lt_b := lt_of_incomp_of_lt hmem val_lt_a
          right
          assumption
        · exact t_ih_rchild hs_hs₂ hmax hmem
#align rbnode.max_is_maximal Rbnode.max_is_maximal

end Rbnode


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import algebra.group.inj_surj
import algebra.group.commute
import algebra.hom.equiv
import algebra.opposites

/-!
# Group structures on the multiplicative and additive opposites
-/
universes u v
variables (α : Type u)

namespace mul_opposite

/-!
### Additive structures on `αᵐᵒᵖ`
-/

instance [add_semigroup α] : add_semigroup (αᵐᵒᵖ) :=
unop_injective.add_semigroup _ (λ x y, rfl)

instance [add_left_cancel_semigroup α] : add_left_cancel_semigroup αᵐᵒᵖ :=
unop_injective.add_left_cancel_semigroup _ (λ x y, rfl)

instance [add_right_cancel_semigroup α] : add_right_cancel_semigroup αᵐᵒᵖ :=
unop_injective.add_right_cancel_semigroup _ (λ x y, rfl)

instance [add_comm_semigroup α] : add_comm_semigroup αᵐᵒᵖ :=
unop_injective.add_comm_semigroup _ (λ x y, rfl)

instance [add_zero_class α] : add_zero_class αᵐᵒᵖ :=
unop_injective.add_zero_class _ rfl (λ x y, rfl)

instance [add_monoid α] : add_monoid αᵐᵒᵖ :=
unop_injective.add_monoid _ rfl (λ _ _, rfl) (λ _ _, rfl)

instance [add_comm_monoid α] : add_comm_monoid αᵐᵒᵖ :=
unop_injective.add_comm_monoid _ rfl (λ _ _, rfl) (λ _ _, rfl)

instance [sub_neg_monoid α] : sub_neg_monoid αᵐᵒᵖ :=
unop_injective.sub_neg_monoid _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

instance [add_group α] : add_group αᵐᵒᵖ :=
unop_injective.add_group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

instance [add_comm_group α] : add_comm_group αᵐᵒᵖ :=
unop_injective.add_comm_group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

/-!
### Multiplicative structures on `αᵐᵒᵖ`

We also generate additive structures on `αᵃᵒᵖ` using `to_additive`
-/

@[to_additive] instance [semigroup α] : semigroup αᵐᵒᵖ :=
{ mul_assoc := λ x y z, unop_injective $ eq.symm $ mul_assoc (unop z) (unop y) (unop x),
  .. mul_opposite.has_mul α }

@[to_additive] instance [right_cancel_semigroup α] : left_cancel_semigroup αᵐᵒᵖ :=
{ mul_left_cancel := λ x y z H, unop_injective $ mul_right_cancel $ op_injective H,
  .. mul_opposite.semigroup α }

@[to_additive] instance [left_cancel_semigroup α] : right_cancel_semigroup αᵐᵒᵖ :=
{ mul_right_cancel := λ x y z H, unop_injective $ mul_left_cancel $ op_injective H,
  .. mul_opposite.semigroup α }

@[to_additive] instance [comm_semigroup α] : comm_semigroup αᵐᵒᵖ :=
{ mul_comm := λ x y, unop_injective $ mul_comm (unop y) (unop x),
  .. mul_opposite.semigroup α }

@[to_additive] instance [mul_one_class α] : mul_one_class αᵐᵒᵖ :=
{ one_mul := λ x, unop_injective $ mul_one $ unop x,
  mul_one := λ x, unop_injective $ one_mul $ unop x,
  .. mul_opposite.has_mul α, .. mul_opposite.has_one α }

@[to_additive] instance [monoid α] : monoid αᵐᵒᵖ :=
{ npow := λ n x, op $ x.unop ^ n,
  npow_zero' := λ x, unop_injective $ monoid.npow_zero' x.unop,
  npow_succ' := λ n x, unop_injective $ pow_succ' x.unop n,
  .. mul_opposite.semigroup α, .. mul_opposite.mul_one_class α }

@[to_additive] instance [right_cancel_monoid α] : left_cancel_monoid αᵐᵒᵖ :=
{ .. mul_opposite.left_cancel_semigroup α, .. mul_opposite.monoid α }

@[to_additive] instance [left_cancel_monoid α] : right_cancel_monoid αᵐᵒᵖ :=
{ .. mul_opposite.right_cancel_semigroup α, .. mul_opposite.monoid α }

@[to_additive] instance [cancel_monoid α] : cancel_monoid αᵐᵒᵖ :=
{ .. mul_opposite.right_cancel_monoid α, .. mul_opposite.left_cancel_monoid α }

@[to_additive] instance [comm_monoid α] : comm_monoid αᵐᵒᵖ :=
{ .. mul_opposite.monoid α, .. mul_opposite.comm_semigroup α }

@[to_additive] instance [cancel_comm_monoid α] : cancel_comm_monoid αᵐᵒᵖ :=
{ .. mul_opposite.cancel_monoid α, .. mul_opposite.comm_monoid α }

@[to_additive] instance [div_inv_monoid α] : div_inv_monoid αᵐᵒᵖ :=
{ zpow := λ n x, op $ x.unop ^ n,
  zpow_zero' := λ x, unop_injective $ div_inv_monoid.zpow_zero' x.unop,
  zpow_succ' := λ n x, unop_injective $
    by rw [unop_op, zpow_of_nat, zpow_of_nat, pow_succ', unop_mul, unop_op],
  zpow_neg' := λ z x, unop_injective $ div_inv_monoid.zpow_neg' z x.unop,
  .. mul_opposite.monoid α, .. mul_opposite.has_inv α }

@[to_additive] instance [group α] : group αᵐᵒᵖ :=
{ mul_left_inv := λ x, unop_injective $ mul_inv_self $ unop x,
  .. mul_opposite.div_inv_monoid α, }

@[to_additive] instance [comm_group α] : comm_group αᵐᵒᵖ :=
{ .. mul_opposite.group α, .. mul_opposite.comm_monoid α }

variable {α}

@[simp, to_additive] lemma unop_div [div_inv_monoid α] (x y : αᵐᵒᵖ) :
  unop (x / y) = (unop y)⁻¹ * unop x :=
rfl

@[simp, to_additive] lemma op_div [div_inv_monoid α] (x y : α) :
  op (x / y) = (op y)⁻¹ * op x :=
by simp [div_eq_mul_inv]

@[simp, to_additive] lemma semiconj_by_op [has_mul α] {a x y : α} :
  semiconj_by (op a) (op y) (op x) ↔ semiconj_by a x y :=
by simp only [semiconj_by, ← op_mul, op_inj, eq_comm]

@[simp, to_additive] lemma semiconj_by_unop [has_mul α] {a x y : αᵐᵒᵖ} :
  semiconj_by (unop a) (unop y) (unop x) ↔ semiconj_by a x y :=
by conv_rhs { rw [← op_unop a, ← op_unop x, ← op_unop y, semiconj_by_op] }

@[to_additive] lemma _root_.semiconj_by.op [has_mul α] {a x y : α} (h : semiconj_by a x y) :
  semiconj_by (op a) (op y) (op x) :=
semiconj_by_op.2 h

@[to_additive] lemma _root_.semiconj_by.unop [has_mul α] {a x y : αᵐᵒᵖ} (h : semiconj_by a x y) :
  semiconj_by (unop a) (unop y) (unop x) :=
semiconj_by_unop.2 h

@[to_additive] lemma _root_.commute.op [has_mul α] {x y : α} (h : commute x y) :
  commute (op x) (op y) := h.op

@[to_additive] lemma commute.unop [has_mul α] {x y : αᵐᵒᵖ} (h : commute x y) :
  commute (unop x) (unop y) := h.unop

@[simp, to_additive] lemma commute_op [has_mul α] {x y : α} :
  commute (op x) (op y) ↔ commute x y :=
semiconj_by_op

@[simp, to_additive] lemma commute_unop [has_mul α] {x y : αᵐᵒᵖ} :
  commute (unop x) (unop y) ↔ commute x y :=
semiconj_by_unop

/-- The function `mul_opposite.op` is an additive equivalence. -/
@[simps { fully_applied := ff, simp_rhs := tt }]
def op_add_equiv [has_add α] : α ≃+ αᵐᵒᵖ :=
{ map_add' := λ a b, rfl, .. op_equiv }

@[simp] lemma op_add_equiv_to_equiv [has_add α] :
  (op_add_equiv : α ≃+ αᵐᵒᵖ).to_equiv = op_equiv :=
rfl

end mul_opposite

/-!
### Multiplicative structures on `αᵃᵒᵖ`
-/

namespace add_opposite

instance [semigroup α] : semigroup (αᵃᵒᵖ) :=
unop_injective.semigroup _ (λ x y, rfl)

instance [left_cancel_semigroup α] : left_cancel_semigroup αᵃᵒᵖ :=
unop_injective.left_cancel_semigroup _ (λ x y, rfl)

instance [right_cancel_semigroup α] : right_cancel_semigroup αᵃᵒᵖ :=
unop_injective.right_cancel_semigroup _ (λ x y, rfl)

instance [comm_semigroup α] : comm_semigroup αᵃᵒᵖ :=
unop_injective.comm_semigroup _ (λ x y, rfl)

instance [mul_one_class α] : mul_one_class αᵃᵒᵖ :=
unop_injective.mul_one_class _ rfl (λ x y, rfl)

instance {β} [has_pow α β] : has_pow αᵃᵒᵖ β := { pow := λ a b, op (unop a ^ b) }

@[simp] lemma op_pow {β} [has_pow α β] (a : α) (b : β) : op (a ^ b) = op a ^ b := rfl
@[simp] lemma unop_pow {β} [has_pow α β] (a : αᵃᵒᵖ) (b : β) : unop (a ^ b) = unop a ^ b := rfl

instance [monoid α] : monoid αᵃᵒᵖ :=
unop_injective.monoid _ rfl (λ _ _, rfl) (λ _ _, rfl)

instance [comm_monoid α] : comm_monoid αᵃᵒᵖ :=
unop_injective.comm_monoid _ rfl (λ _ _, rfl) (λ _ _, rfl)

instance [div_inv_monoid α] : div_inv_monoid αᵃᵒᵖ :=
unop_injective.div_inv_monoid _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

instance [group α] : group αᵃᵒᵖ :=
unop_injective.group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

instance [comm_group α] : comm_group αᵃᵒᵖ :=
unop_injective.comm_group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

variable {α}

/-- The function `add_opposite.op` is a multiplicative equivalence. -/
@[simps { fully_applied := ff, simp_rhs := tt }]
def op_mul_equiv [has_mul α] : α ≃* αᵃᵒᵖ :=
{ map_mul' := λ a b, rfl, .. op_equiv }

@[simp] lemma op_mul_equiv_to_equiv [has_mul α] :
  (op_mul_equiv : α ≃* αᵃᵒᵖ).to_equiv = op_equiv :=
rfl

end add_opposite

open mul_opposite

/-- Inversion on a group is a `mul_equiv` to the opposite group. When `G` is commutative, there is
`mul_equiv.inv`. -/
@[to_additive "Negation on an additive group is an `add_equiv` to the opposite group. When `G`
is commutative, there is `add_equiv.inv`.", simps { fully_applied := ff, simp_rhs := tt }]
def mul_equiv.inv' (G : Type*) [group G] : G ≃* Gᵐᵒᵖ :=
{ map_mul' := λ x y, unop_injective $ mul_inv_rev x y,
  .. (equiv.inv G).trans op_equiv }

/-- A monoid homomorphism `f : M →* N` such that `f x` commutes with `f y` for all `x, y` defines
a monoid homomorphism to `Nᵐᵒᵖ`. -/
@[to_additive "An additive monoid homomorphism `f : M →+ N` such that `f x` additively commutes
with `f y` for all `x, y` defines an additive monoid homomorphism to `Sᵃᵒᵖ`.",
  simps {fully_applied := ff}]
def monoid_hom.to_opposite {M N : Type*} [mul_one_class M] [mul_one_class N] (f : M →* N)
  (hf : ∀ x y, commute (f x) (f y)) : M →* Nᵐᵒᵖ :=
{ to_fun := mul_opposite.op ∘ f,
  map_one' := congr_arg op f.map_one,
  map_mul' := λ x y, by simp [(hf x y).eq] }

/-- A monoid homomorphism `f : M →* N` such that `f x` commutes with `f y` for all `x, y` defines
a monoid homomorphism from `Mᵐᵒᵖ`. -/
@[to_additive "An additive monoid homomorphism `f : M →+ N` such that `f x` additively commutes
with `f y` for all `x`, `y` defines an additive monoid homomorphism from `Mᵃᵒᵖ`.",
  simps {fully_applied := ff}]
def monoid_hom.from_opposite {M N : Type*} [mul_one_class M] [mul_one_class N] (f : M →* N)
  (hf : ∀ x y, commute (f x) (f y)) : Mᵐᵒᵖ →* N :=
{ to_fun := f ∘ mul_opposite.unop,
  map_one' := f.map_one,
  map_mul' := λ x y, (f.map_mul _ _).trans (hf _ _).eq }

/-- The units of the opposites are equivalent to the opposites of the units. -/
@[to_additive "The additive units of the additive opposites are equivalent to the additive opposites
of the additive units."]
def units.op_equiv {M} [monoid M] : (Mᵐᵒᵖ)ˣ ≃* (Mˣ)ᵐᵒᵖ :=
{ to_fun := λ u, op ⟨unop u, unop ↑(u⁻¹), op_injective u.4, op_injective u.3⟩,
  inv_fun := mul_opposite.rec $ λ u, ⟨op ↑(u), op ↑(u⁻¹), unop_injective $ u.4, unop_injective u.3⟩,
  map_mul' := λ x y, unop_injective $ units.ext $ rfl,
  left_inv := λ x, units.ext $ by simp,
  right_inv := λ x, unop_injective $ units.ext $ rfl }

@[simp, to_additive]
lemma units.coe_unop_op_equiv {M} [monoid M] (u : (Mᵐᵒᵖ)ˣ) :
  ((units.op_equiv u).unop : M) = unop (u : Mᵐᵒᵖ) :=
rfl

@[simp, to_additive]
lemma units.coe_op_equiv_symm {M} [monoid M] (u : (Mˣ)ᵐᵒᵖ) :
  (units.op_equiv.symm u : Mᵐᵒᵖ) = op (u.unop : M) :=
rfl

/-- A monoid homomorphism `M →* N` can equivalently be viewed as a monoid homomorphism
`Mᵐᵒᵖ →* Nᵐᵒᵖ`. This is the action of the (fully faithful) `ᵐᵒᵖ`-functor on morphisms. -/
@[to_additive "An additive monoid homomorphism `M →+ N` can equivalently be viewed as an
additive monoid homomorphism `Mᵃᵒᵖ →+ Nᵃᵒᵖ`. This is the action of the (fully faithful)
`ᵃᵒᵖ`-functor on morphisms.", simps]
def monoid_hom.op {M N} [mul_one_class M] [mul_one_class N] :
  (M →* N) ≃ (Mᵐᵒᵖ →* Nᵐᵒᵖ) :=
{ to_fun    := λ f, { to_fun   := op ∘ f ∘ unop,
                      map_one' := congr_arg op f.map_one,
                      map_mul' := λ x y, unop_injective (f.map_mul y.unop x.unop) },
  inv_fun   := λ f, { to_fun   := unop ∘ f ∘ op,
                      map_one' := congr_arg unop f.map_one,
                      map_mul' := λ x y, congr_arg unop (f.map_mul (op y) (op x)) },
  left_inv  := λ f, by { ext, refl },
  right_inv := λ f, by { ext x, simp } }

/-- The 'unopposite' of a monoid homomorphism `Mᵐᵒᵖ →* Nᵐᵒᵖ`. Inverse to `monoid_hom.op`. -/
@[simp, to_additive "The 'unopposite' of an additive monoid homomorphism `Mᵃᵒᵖ →+ Nᵃᵒᵖ`. Inverse to
`add_monoid_hom.op`."]
def monoid_hom.unop {M N} [mul_one_class M] [mul_one_class N] :
  (Mᵐᵒᵖ →* Nᵐᵒᵖ) ≃ (M →* N) := monoid_hom.op.symm

/-- An additive homomorphism `M →+ N` can equivalently be viewed as an additive homomorphism
`Mᵐᵒᵖ →+ Nᵐᵒᵖ`. This is the action of the (fully faithful) `ᵐᵒᵖ`-functor on morphisms. -/
@[simps]
def add_monoid_hom.mul_op {M N} [add_zero_class M] [add_zero_class N] :
  (M →+ N) ≃ (Mᵐᵒᵖ →+ Nᵐᵒᵖ) :=
{ to_fun    := λ f, { to_fun    := op ∘ f ∘ unop,
                      map_zero' := unop_injective f.map_zero,
                      map_add'  := λ x y, unop_injective (f.map_add x.unop y.unop) },
  inv_fun   := λ f, { to_fun    := unop ∘ f ∘ op,
                      map_zero' := congr_arg unop f.map_zero,
                      map_add'  := λ x y, congr_arg unop (f.map_add (op x) (op y)) },
  left_inv  := λ f, by { ext, refl },
  right_inv := λ f, by { ext, simp } }

/-- The 'unopposite' of an additive monoid hom `αᵐᵒᵖ →+ βᵐᵒᵖ`. Inverse to
`add_monoid_hom.mul_op`. -/
@[simp] def add_monoid_hom.mul_unop {α β} [add_zero_class α] [add_zero_class β] :
  (αᵐᵒᵖ →+ βᵐᵒᵖ) ≃ (α →+ β) := add_monoid_hom.mul_op.symm

/-- A iso `α ≃+ β` can equivalently be viewed as an iso `αᵐᵒᵖ ≃+ βᵐᵒᵖ`. -/
@[simps]
def add_equiv.mul_op {α β} [has_add α] [has_add β] :
  (α ≃+ β) ≃ (αᵐᵒᵖ ≃+ βᵐᵒᵖ) :=
{ to_fun    := λ f, op_add_equiv.symm.trans (f.trans op_add_equiv),
  inv_fun   := λ f, op_add_equiv.trans (f.trans op_add_equiv.symm),
  left_inv  := λ f, by { ext, refl },
  right_inv := λ f, by { ext, simp } }

/-- The 'unopposite' of an iso `αᵐᵒᵖ ≃+ βᵐᵒᵖ`. Inverse to `add_equiv.mul_op`. -/
@[simp] def add_equiv.mul_unop {α β} [has_add α] [has_add β] :
  (αᵐᵒᵖ ≃+ βᵐᵒᵖ) ≃ (α ≃+ β) := add_equiv.mul_op.symm

/-- A iso `α ≃* β` can equivalently be viewed as an iso `αᵐᵒᵖ ≃* βᵐᵒᵖ`. -/
@[to_additive "A iso `α ≃+ β` can equivalently be viewed as an iso `αᵃᵒᵖ ≃+ βᵃᵒᵖ`.", simps]
def mul_equiv.op {α β} [has_mul α] [has_mul β] :
  (α ≃* β) ≃ (αᵐᵒᵖ ≃* βᵐᵒᵖ) :=
{ to_fun    := λ f, { to_fun   := op ∘ f ∘ unop,
                      inv_fun  := op ∘ f.symm ∘ unop,
                      left_inv := λ x, unop_injective (f.symm_apply_apply x.unop),
                      right_inv := λ x, unop_injective (f.apply_symm_apply x.unop),
                      map_mul' := λ x y, unop_injective (f.map_mul y.unop x.unop) },
  inv_fun   := λ f, { to_fun   := unop ∘ f ∘ op,
                      inv_fun  := unop ∘ f.symm ∘ op,
                      left_inv := λ x, by simp,
                      right_inv := λ x, by simp,
                      map_mul' := λ x y, congr_arg unop (f.map_mul (op y) (op x)) },
  left_inv  := λ f, by { ext, refl },
  right_inv := λ f, by { ext, simp } }

/-- The 'unopposite' of an iso `αᵐᵒᵖ ≃* βᵐᵒᵖ`. Inverse to `mul_equiv.op`. -/
@[simp, to_additive "The 'unopposite' of an iso `αᵃᵒᵖ ≃+ βᵃᵒᵖ`. Inverse to `add_equiv.op`."]
def mul_equiv.unop {α β} [has_mul α] [has_mul β] :
  (αᵐᵒᵖ ≃* βᵐᵒᵖ) ≃ (α ≃* β) := mul_equiv.op.symm

section ext

/-- This ext lemma change equalities on `αᵐᵒᵖ →+ β` to equalities on `α →+ β`.
This is useful because there are often ext lemmas for specific `α`s that will apply
to an equality of `α →+ β` such as `finsupp.add_hom_ext'`. -/
@[ext]
lemma add_monoid_hom.mul_op_ext {α β} [add_zero_class α] [add_zero_class β]
  (f g : αᵐᵒᵖ →+ β)
  (h : f.comp (op_add_equiv : α ≃+ αᵐᵒᵖ).to_add_monoid_hom =
       g.comp (op_add_equiv : α ≃+ αᵐᵒᵖ).to_add_monoid_hom) : f = g :=
add_monoid_hom.ext $ mul_opposite.rec $ λ x, (add_monoid_hom.congr_fun h : _) x

end ext