/-
Copyright (c) 2018 Louis Carlin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Louis Carlin, Mario Carneiro

! This file was ported from Lean 3 source module algebra.euclidean_domain.defs
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Logic.Nontrivial
import Mathbin.Algebra.Divisibility.Basic
import Mathbin.Algebra.Group.Basic
import Mathbin.Algebra.Ring.Defs

/-!
# Euclidean domains

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

This file introduces Euclidean domains and provides the extended Euclidean algorithm. To be precise,
a slightly more general version is provided which is sometimes called a transfinite Euclidean domain
and differs in the fact that the degree function need not take values in `ℕ` but can take values in
any well-ordered set. Transfinite Euclidean domains were introduced by Motzkin and examples which
don't satisfy the classical notion were provided independently by Hiblot and Nagata.

## Main definitions

* `euclidean_domain`: Defines Euclidean domain with functions `quotient` and `remainder`. Instances
  of `has_div` and `has_mod` are provided, so that one can write `a = b * (a / b) + a % b`.
* `gcd`: defines the greatest common divisors of two elements of a Euclidean domain.
* `xgcd`: given two elements `a b : R`, `xgcd a b` defines the pair `(x, y)` such that
  `x * a + y * b = gcd a b`.
* `lcm`: defines the lowest common multiple of two elements `a` and `b` of a Euclidean domain as
  `a * b / (gcd a b)`

## Main statements

See `algebra.euclidean_domain.basic` for most of the theorems about Eucliean domains,
including Bézout's lemma.

See `algebra.euclidean_domain.instances` for that facts that `ℤ` is a Euclidean domain,
as is any field.

## Notation

`≺` denotes the well founded relation on the Euclidean domain, e.g. in the example of the polynomial
ring over a field, `p ≺ q` for polynomials `p` and `q` if and only if the degree of `p` is less than
the degree of `q`.

## Implementation details

Instead of working with a valuation, `euclidean_domain` is implemented with the existence of a well
founded relation `r` on the integral domain `R`, which in the example of `ℤ` would correspond to
setting `i ≺ j` for integers `i` and `j` if the absolute value of `i` is smaller than the absolute
value of `j`.

## References

* [Th. Motzkin, *The Euclidean algorithm*][MR32592]
* [J.-J. Hiblot, *Des anneaux euclidiens dont le plus petit algorithme n'est pas à valeurs finies*]
  [MR399081]
* [M. Nagata, *On Euclid algorithm*][MR541021]

## Tags

Euclidean domain, transfinite Euclidean domain, Bézout's lemma
-/

universe u

#print EuclideanDomain /-
/-- A `euclidean_domain` is an non-trivial commutative ring with a division and a remainder,
  satisfying `b * (a / b) + a % b = a`.
  The definition of a euclidean domain usually includes a valuation function `R → ℕ`.
  This definition is slightly generalised to include a well founded relation
  `r` with the property that `r (a % b) b`, instead of a valuation.  -/
@[protect_proj without mul_left_not_lt r_wellFounded]
class EuclideanDomain (R : Type u) extends CommRing R, Nontrivial R where
  Quotient : R → R → R
  quotient_zero : ∀ a, Quotient a 0 = 0
  remainder : R → R → R
  quotient_mul_add_remainder_eq : ∀ a b, b * Quotient a b + remainder a b = a
  R : R → R → Prop
  r_wellFounded : WellFounded r
  remainder_lt : ∀ (a) {b}, b ≠ 0 → r (remainder a b) b
  mul_left_not_lt : ∀ (a) {b}, b ≠ 0 → ¬r (a * b) a
#align euclidean_domain EuclideanDomain
-/

namespace EuclideanDomain

variable {R : Type u}

variable [EuclideanDomain R]

-- mathport name: «expr ≺ »
local infixl:50 " ≺ " => EuclideanDomain.r

-- see Note [lower instance priority]
instance (priority := 70) : Div R :=
  ⟨EuclideanDomain.quotient⟩

-- see Note [lower instance priority]
instance (priority := 70) : Mod R :=
  ⟨EuclideanDomain.remainder⟩

/- warning: euclidean_domain.div_add_mod -> EuclideanDomain.div_add_mod is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R) (b : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) b (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.hasDiv.{u1} R _inst_1)) a b)) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_1)) a b)) a
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R) (b : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) b (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.instDiv.{u1} R _inst_1)) a b)) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_1)) a b)) a
Case conversion may be inaccurate. Consider using '#align euclidean_domain.div_add_mod EuclideanDomain.div_add_modₓ'. -/
theorem div_add_mod (a b : R) : b * (a / b) + a % b = a :=
  EuclideanDomain.quotient_mul_add_remainder_eq _ _
#align euclidean_domain.div_add_mod EuclideanDomain.div_add_mod

/- warning: euclidean_domain.mod_add_div -> EuclideanDomain.mod_add_div is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R) (b : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_1)) a b) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) b (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.hasDiv.{u1} R _inst_1)) a b))) a
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R) (b : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_1)) a b) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) b (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.instDiv.{u1} R _inst_1)) a b))) a
Case conversion may be inaccurate. Consider using '#align euclidean_domain.mod_add_div EuclideanDomain.mod_add_divₓ'. -/
theorem mod_add_div (a b : R) : a % b + b * (a / b) = a :=
  (add_comm _ _).trans (div_add_mod _ _)
#align euclidean_domain.mod_add_div EuclideanDomain.mod_add_div

/- warning: euclidean_domain.mod_add_div' -> EuclideanDomain.mod_add_div' is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (m : R) (k : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_1)) m k) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.hasDiv.{u1} R _inst_1)) m k) k)) m
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (m : R) (k : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_1)) m k) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.instDiv.{u1} R _inst_1)) m k) k)) m
Case conversion may be inaccurate. Consider using '#align euclidean_domain.mod_add_div' EuclideanDomain.mod_add_div'ₓ'. -/
theorem mod_add_div' (m k : R) : m % k + m / k * k = m :=
  by
  rw [mul_comm]
  exact mod_add_div _ _
#align euclidean_domain.mod_add_div' EuclideanDomain.mod_add_div'

/- warning: euclidean_domain.div_add_mod' -> EuclideanDomain.div_add_mod' is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (m : R) (k : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.hasDiv.{u1} R _inst_1)) m k) k) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_1)) m k)) m
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (m : R) (k : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.instDiv.{u1} R _inst_1)) m k) k) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_1)) m k)) m
Case conversion may be inaccurate. Consider using '#align euclidean_domain.div_add_mod' EuclideanDomain.div_add_mod'ₓ'. -/
theorem div_add_mod' (m k : R) : m / k * k + m % k = m :=
  by
  rw [mul_comm]
  exact div_add_mod _ _
#align euclidean_domain.div_add_mod' EuclideanDomain.div_add_mod'

/- warning: euclidean_domain.mod_eq_sub_mul_div -> EuclideanDomain.mod_eq_sub_mul_div is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_2 : EuclideanDomain.{u1} R] (a : R) (b : R), Eq.{succ u1} R (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_2)) a b) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toHasSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_2)))))))) a (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_2))))) b (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.hasDiv.{u1} R _inst_2)) a b)))
but is expected to have type
  forall {R : Type.{u1}} [_inst_2 : EuclideanDomain.{u1} R] (a : R) (b : R), Eq.{succ u1} R (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_2)) a b) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (Ring.toSub.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_2)))) a (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_2)))))) b (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.instDiv.{u1} R _inst_2)) a b)))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.mod_eq_sub_mul_div EuclideanDomain.mod_eq_sub_mul_divₓ'. -/
theorem mod_eq_sub_mul_div {R : Type _} [EuclideanDomain R] (a b : R) : a % b = a - b * (a / b) :=
  calc
    a % b = b * (a / b) + a % b - b * (a / b) := (add_sub_cancel' _ _).symm
    _ = a - b * (a / b) := by rw [div_add_mod]

#align euclidean_domain.mod_eq_sub_mul_div EuclideanDomain.mod_eq_sub_mul_div

/- warning: euclidean_domain.mod_lt -> EuclideanDomain.mod_lt is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R) {b : R}, (Ne.{succ u1} R b (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))) -> (EuclideanDomain.r.{u1} R _inst_1 (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_1)) a b) b)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R) {b : R}, (Ne.{succ u1} R b (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) -> (EuclideanDomain.r.{u1} R _inst_1 (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_1)) a b) b)
Case conversion may be inaccurate. Consider using '#align euclidean_domain.mod_lt EuclideanDomain.mod_ltₓ'. -/
theorem mod_lt : ∀ (a) {b : R}, b ≠ 0 → a % b ≺ b :=
  EuclideanDomain.remainder_lt
#align euclidean_domain.mod_lt EuclideanDomain.mod_lt

/- warning: euclidean_domain.mul_right_not_lt -> EuclideanDomain.mul_right_not_lt is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] {a : R} (b : R), (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))) -> (Not (EuclideanDomain.r.{u1} R _inst_1 (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) a b) b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] {a : R} (b : R), (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) -> (Not (EuclideanDomain.r.{u1} R _inst_1 (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) a b) b))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.mul_right_not_lt EuclideanDomain.mul_right_not_ltₓ'. -/
theorem mul_right_not_lt {a : R} (b) (h : a ≠ 0) : ¬a * b ≺ b :=
  by
  rw [mul_comm]
  exact mul_left_not_lt b h
#align euclidean_domain.mul_right_not_lt EuclideanDomain.mul_right_not_lt

/- warning: euclidean_domain.mod_zero -> EuclideanDomain.mod_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R), Eq.{succ u1} R (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_1)) a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))) a
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R), Eq.{succ u1} R (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_1)) a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) a
Case conversion may be inaccurate. Consider using '#align euclidean_domain.mod_zero EuclideanDomain.mod_zeroₓ'. -/
@[simp]
theorem mod_zero (a : R) : a % 0 = a := by
  simpa only [MulZeroClass.zero_mul, zero_add] using div_add_mod a 0
#align euclidean_domain.mod_zero EuclideanDomain.mod_zero

/- warning: euclidean_domain.lt_one -> EuclideanDomain.lt_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R), (EuclideanDomain.r.{u1} R _inst_1 a (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))))) -> (Eq.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R), (EuclideanDomain.r.{u1} R _inst_1 a (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (NonAssocRing.toOne.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) -> (Eq.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.lt_one EuclideanDomain.lt_oneₓ'. -/
theorem lt_one (a : R) : a ≺ (1 : R) → a = 0 :=
  haveI := Classical.dec
  not_imp_not.1 fun h => by simpa only [one_mul] using mul_left_not_lt 1 h
#align euclidean_domain.lt_one EuclideanDomain.lt_one

/- warning: euclidean_domain.val_dvd_le -> EuclideanDomain.val_dvd_le is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R) (b : R), (Dvd.Dvd.{u1} R (semigroupDvd.{u1} R (SemigroupWithZero.toSemigroup.{u1} R (NonUnitalSemiring.toSemigroupWithZero.{u1} R (NonUnitalRing.toNonUnitalSemiring.{u1} R (NonUnitalCommRing.toNonUnitalRing.{u1} R (CommRing.toNonUnitalCommRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) b a) -> (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))) -> (Not (EuclideanDomain.r.{u1} R _inst_1 a b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R) (b : R), (Dvd.dvd.{u1} R (semigroupDvd.{u1} R (SemigroupWithZero.toSemigroup.{u1} R (NonUnitalSemiring.toSemigroupWithZero.{u1} R (NonUnitalRing.toNonUnitalSemiring.{u1} R (NonUnitalCommRing.toNonUnitalRing.{u1} R (CommRing.toNonUnitalCommRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) b a) -> (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) -> (Not (EuclideanDomain.r.{u1} R _inst_1 a b))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.val_dvd_le EuclideanDomain.val_dvd_leₓ'. -/
theorem val_dvd_le : ∀ a b : R, b ∣ a → a ≠ 0 → ¬a ≺ b
  | _, b, ⟨d, rfl⟩, ha =>
    mul_left_not_lt b
      (mt
        (by
          rintro rfl
          exact MulZeroClass.mul_zero _)
        ha)
#align euclidean_domain.val_dvd_le EuclideanDomain.val_dvd_le

/- warning: euclidean_domain.div_zero -> EuclideanDomain.div_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R), Eq.{succ u1} R (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.hasDiv.{u1} R _inst_1)) a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] (a : R), Eq.{succ u1} R (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.instDiv.{u1} R _inst_1)) a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.div_zero EuclideanDomain.div_zeroₓ'. -/
@[simp]
theorem div_zero (a : R) : a / 0 = 0 :=
  EuclideanDomain.quotient_zero a
#align euclidean_domain.div_zero EuclideanDomain.div_zero

section

open Classical

/- warning: euclidean_domain.gcd.induction -> EuclideanDomain.GCD.induction is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] {P : R -> R -> Prop} (a : R) (b : R), (forall (x : R), P (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))))) x) -> (forall (a : R) (b : R), (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))) -> (P (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_1)) b a) a) -> (P a b)) -> (P a b)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] {P : R -> R -> Prop} (a : R) (b : R), (forall (x : R), P (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) x) -> (forall (a : R) (b : R), (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) -> (P (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_1)) b a) a) -> (P a b)) -> (P a b)
Case conversion may be inaccurate. Consider using '#align euclidean_domain.gcd.induction EuclideanDomain.GCD.inductionₓ'. -/
@[elab_as_elim]
theorem GCD.induction {P : R → R → Prop} :
    ∀ a b : R, (∀ x, P 0 x) → (∀ a b, a ≠ 0 → P (b % a) a → P a b) → P a b
  | a => fun b H0 H1 =>
    if a0 : a = 0 then a0.symm ▸ H0 _
    else
      have h := mod_lt b a0
      H1 _ _ a0 (gcd.induction (b % a) a H0 H1)termination_by'
  ⟨_, r_well_founded⟩
#align euclidean_domain.gcd.induction EuclideanDomain.GCD.induction

end

section Gcd

variable [DecidableEq R]

#print EuclideanDomain.gcd /-
/-- `gcd a b` is a (non-unique) element such that `gcd a b ∣ a` `gcd a b ∣ b`, and for
  any element `c` such that `c ∣ a` and `c ∣ b`, then `c ∣ gcd a b` -/
def gcd : R → R → R
  | a => fun b =>
    if a0 : a = 0 then b
    else
      have h := mod_lt b a0
      gcd (b % a) a termination_by'
  ⟨_, r_well_founded⟩
#align euclidean_domain.gcd EuclideanDomain.gcd
-/

/- warning: euclidean_domain.gcd_zero_left -> EuclideanDomain.gcd_zero_left is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] (a : R), Eq.{succ u1} R (EuclideanDomain.gcd.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))))) a) a
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] (a : R), Eq.{succ u1} R (EuclideanDomain.gcd.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) a) a
Case conversion may be inaccurate. Consider using '#align euclidean_domain.gcd_zero_left EuclideanDomain.gcd_zero_leftₓ'. -/
@[simp]
theorem gcd_zero_left (a : R) : gcd 0 a = a :=
  by
  rw [gcd]
  exact if_pos rfl
#align euclidean_domain.gcd_zero_left EuclideanDomain.gcd_zero_left

#print EuclideanDomain.xgcdAux /-
/-- An implementation of the extended GCD algorithm.
At each step we are computing a triple `(r, s, t)`, where `r` is the next value of the GCD
algorithm, to compute the greatest common divisor of the input (say `x` and `y`), and `s` and `t`
are the coefficients in front of `x` and `y` to obtain `r` (i.e. `r = s * x + t * y`).
The function `xgcd_aux` takes in two triples, and from these recursively computes the next triple:
```
xgcd_aux (r, s, t) (r', s', t') = xgcd_aux (r' % r, s' - (r' / r) * s, t' - (r' / r) * t) (r, s, t)
```
-/
def xgcdAux : R → R → R → R → R → R → R × R × R
  | r => fun s t r' s' t' =>
    if hr : r = 0 then (r', s', t')
    else
      have : r' % r ≺ r := mod_lt _ hr
      let q := r' / r
      xgcd_aux (r' % r) (s' - q * s) (t' - q * t) r s t termination_by'
  ⟨_, r_well_founded⟩
#align euclidean_domain.xgcd_aux EuclideanDomain.xgcdAux
-/

/- warning: euclidean_domain.xgcd_zero_left -> EuclideanDomain.xgcd_zero_left is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] {s : R} {t : R} {r' : R} {s' : R} {t' : R}, Eq.{succ u1} (Prod.{u1, u1} R (Prod.{u1, u1} R R)) (EuclideanDomain.xgcdAux.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))))) s t r' s' t') (Prod.mk.{u1, u1} R (Prod.{u1, u1} R R) r' (Prod.mk.{u1, u1} R R s' t'))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] {s : R} {t : R} {r' : R} {s' : R} {t' : R}, Eq.{succ u1} (Prod.{u1, u1} R (Prod.{u1, u1} R R)) (EuclideanDomain.xgcdAux.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) s t r' s' t') (Prod.mk.{u1, u1} R (Prod.{u1, u1} R R) r' (Prod.mk.{u1, u1} R R s' t'))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.xgcd_zero_left EuclideanDomain.xgcd_zero_leftₓ'. -/
@[simp]
theorem xgcd_zero_left {s t r' s' t' : R} : xgcdAux 0 s t r' s' t' = (r', s', t') :=
  by
  unfold xgcd_aux
  exact if_pos rfl
#align euclidean_domain.xgcd_zero_left EuclideanDomain.xgcd_zero_left

/- warning: euclidean_domain.xgcd_aux_rec -> EuclideanDomain.xgcdAux_rec is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] {r : R} {s : R} {t : R} {r' : R} {s' : R} {t' : R}, (Ne.{succ u1} R r (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))) -> (Eq.{succ u1} (Prod.{u1, u1} R (Prod.{u1, u1} R R)) (EuclideanDomain.xgcdAux.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) r s t r' s' t') (EuclideanDomain.xgcdAux.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.hasMod.{u1} R _inst_1)) r' r) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toHasSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))) s' (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.hasDiv.{u1} R _inst_1)) r' r) s)) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toHasSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))) t' (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))) (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.hasDiv.{u1} R _inst_1)) r' r) t)) r s t))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] {r : R} {s : R} {t : R} {r' : R} {s' : R} {t' : R}, (Ne.{succ u1} R r (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))) -> (Eq.{succ u1} (Prod.{u1, u1} R (Prod.{u1, u1} R R)) (EuclideanDomain.xgcdAux.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) r s t r' s' t') (EuclideanDomain.xgcdAux.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (HMod.hMod.{u1, u1, u1} R R R (instHMod.{u1} R (EuclideanDomain.instMod.{u1} R _inst_1)) r' r) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (Ring.toSub.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))) s' (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.instDiv.{u1} R _inst_1)) r' r) s)) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (Ring.toSub.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))) t' (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) (HDiv.hDiv.{u1, u1, u1} R R R (instHDiv.{u1} R (EuclideanDomain.instDiv.{u1} R _inst_1)) r' r) t)) r s t))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.xgcd_aux_rec EuclideanDomain.xgcdAux_recₓ'. -/
theorem xgcdAux_rec {r s t r' s' t' : R} (h : r ≠ 0) :
    xgcdAux r s t r' s' t' = xgcdAux (r' % r) (s' - r' / r * s) (t' - r' / r * t) r s t :=
  by
  conv =>
    lhs
    rw [xgcd_aux]
  exact if_neg h
#align euclidean_domain.xgcd_aux_rec EuclideanDomain.xgcdAux_rec

#print EuclideanDomain.xgcd /-
/-- Use the extended GCD algorithm to generate the `a` and `b` values
  satisfying `gcd x y = x * a + y * b`. -/
def xgcd (x y : R) : R × R :=
  (xgcdAux x 1 0 y 0 1).2
#align euclidean_domain.xgcd EuclideanDomain.xgcd
-/

#print EuclideanDomain.gcdA /-
/-- The extended GCD `a` value in the equation `gcd x y = x * a + y * b`. -/
def gcdA (x y : R) : R :=
  (xgcd x y).1
#align euclidean_domain.gcd_a EuclideanDomain.gcdA
-/

#print EuclideanDomain.gcdB /-
/-- The extended GCD `b` value in the equation `gcd x y = x * a + y * b`. -/
def gcdB (x y : R) : R :=
  (xgcd x y).2
#align euclidean_domain.gcd_b EuclideanDomain.gcdB
-/

/- warning: euclidean_domain.gcd_a_zero_left -> EuclideanDomain.gcdA_zero_left is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] {s : R}, Eq.{succ u1} R (EuclideanDomain.gcdA.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))))) s) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] {s : R}, Eq.{succ u1} R (EuclideanDomain.gcdA.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) s) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.gcd_a_zero_left EuclideanDomain.gcdA_zero_leftₓ'. -/
@[simp]
theorem gcdA_zero_left {s : R} : gcdA 0 s = 0 :=
  by
  unfold gcd_a
  rw [xgcd, xgcd_zero_left]
#align euclidean_domain.gcd_a_zero_left EuclideanDomain.gcdA_zero_left

/- warning: euclidean_domain.gcd_b_zero_left -> EuclideanDomain.gcdB_zero_left is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] {s : R}, Eq.{succ u1} R (EuclideanDomain.gcdB.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))))) s) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : EuclideanDomain.{u1} R] [_inst_2 : DecidableEq.{succ u1} R] {s : R}, Eq.{succ u1} R (EuclideanDomain.gcdB.{u1} R _inst_1 (fun (a : R) (b : R) => _inst_2 a b) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R (CommRing.toCommSemiring.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1)))))) s) (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (NonAssocRing.toOne.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R (EuclideanDomain.toCommRing.{u1} R _inst_1))))))
Case conversion may be inaccurate. Consider using '#align euclidean_domain.gcd_b_zero_left EuclideanDomain.gcdB_zero_leftₓ'. -/
@[simp]
theorem gcdB_zero_left {s : R} : gcdB 0 s = 1 :=
  by
  unfold gcd_b
  rw [xgcd, xgcd_zero_left]
#align euclidean_domain.gcd_b_zero_left EuclideanDomain.gcdB_zero_left

#print EuclideanDomain.xgcd_val /-
theorem xgcd_val (x y : R) : xgcd x y = (gcdA x y, gcdB x y) :=
  Prod.mk.eta.symm
#align euclidean_domain.xgcd_val EuclideanDomain.xgcd_val
-/

end Gcd

section Lcm

variable [DecidableEq R]

#print EuclideanDomain.lcm /-
/-- `lcm a b` is a (non-unique) element such that `a ∣ lcm a b` `b ∣ lcm a b`, and for
  any element `c` such that `a ∣ c` and `b ∣ c`, then `lcm a b ∣ c` -/
def lcm (x y : R) : R :=
  x * y / gcd x y
#align euclidean_domain.lcm EuclideanDomain.lcm
-/

end Lcm

end EuclideanDomain


import tactic.linarith data.real.basic

local notation `|` x `|` := abs x

lemma zero_of_abs_lt_all (x : ℝ) (h : ∀ ε > 0, |x| < ε) : x = 0 :=
eq_zero_of_abs_eq_zero $ eq_of_le_of_forall_le_of_dense (abs_nonneg x) $ λ ε ε_pos, le_of_lt (h ε ε_pos)

-- The next few things should be hidden
@[user_attribute]
meta def ineq_rules : user_attribute :=
{ name := `ineq_rules,
  descr := "lemmas usable to prove inequalities" }

attribute [ineq_rules] add_lt_add le_max_left le_max_right

meta def obvious_ineq := `[linarith <|> apply_rules ineq_rules]
run_cmd add_interactive [`obvious_ineq]
-- end of scary things


import game.sets.sets_level04 -- hide

namespace xena -- hide

open_locale classical -- hide

variable X : Type --hide

/-
# Chapter 1 : Sets

## Level 5
-/

/-
If `h : ∀ (x : X), x ∈ A → x ∈ B` then `h` is a function which takes
a term `x` of type `X` as input, and also a proof that `x ∈ A`, and outputs a
proof that `x ∈ B`. If you want to run this function `h` on some term `a : X`
then any of the following work:

```
have h2 := h a,
replace h := h a,
specialize h a
```

The first one preserves `h` and creates a new hypothesis `h2`. The others
replace `h` with `h : a ∈ A → a ∈ B`.
-/

/- Lemma
If $A$ and $B$ are sets of any type $X$, then
$$ A \subseteq B \iff A \cap B = A.$$
-/

theorem subset_iff_inter_eq (A : set X) (B : set X) : A ⊆ B ↔ A = A ∩ B :=
begin
  rw subset_iff,
  rw ext_iff,
  split,
  { intros h x,
    specialize h x,
    rw mem_inter_iff,
    tauto!
  },
  { intros h x hx,
    specialize h x,
    rw mem_inter_iff at h,
    tauto!,
  }
end

-- begin hide
-- theorem subset_iff_intersection_eq' (A : set X) (B : set X) : A ⊆ B ↔ A ∩ B = A :=
-- begin
--   rw subset_iff,
--   rw eq_iff,
--   apply forall_congr, -- clever trick
--   intro x,
--   rw mem_inter_iff, -- no longer under a binder
--   tauto!
-- end
-- end hide

end xena -- hide


import trim

noncomputable theory
open_locale classical measure_theory nnreal ennreal

variables {α : Type*} {m n : measurable_space α}

namespace measure_theory

/-- The conditional expectation of an integrable function `f` on a sub-σ-algebra `m` of
`n` is the Radon-Nikodym derivative between `(μ.with_density_signed_measure f)` restricted
on `n` and `μ` restricted on `n`.

Note the ordering where we take the density before the restriction. This is
because in this case, we will only need integrability on `n` instead `m` which is a
weaker condition (integrability requies measurabililty almost everywhere). -/
def condexp {m n : measurable_space α} {μ : measure α} (hle : m ≤ n)
  (f : α → ℝ) (hfi : integrable f μ) : α → ℝ :=
signed_measure.radon_nikodym_deriv
  ((μ.with_density_signed_measure f).trim hle) (μ.trim hle)

namespace condexp

open signed_measure measure

-- possible to change to `sigma_finite`?
variables {μ : measure α} [is_finite_measure μ] {a b : ℝ} {f g : α → ℝ} (hle : m ≤ n)
  (hfi : integrable f μ) (hgi : integrable g μ)

section

include hle hfi

lemma measurable : measurable[m] (condexp hle f hfi) :=
measurable_radon_nikodym_deriv _ _

lemma integrable : @integrable ℝ _ _ α m (condexp hle f hfi) (μ.trim hle) :=
integrable_radon_nikodym_deriv _ _

lemma condexp_spec {i : set α} (hi : measurable_set[m] i) :
  ∫ x in i, condexp hle f hfi x ∂(μ.trim hle) = ∫ x in i, f x ∂μ :=
begin
  rw [← with_density_signed_measure_trim_eq_integral hle hfi hi,
      ← signed_measure.with_density_radon_nikodym_deriv_eq
        ((μ.with_density_signed_measure f).trim hle) (μ.trim hle) _,
      ← condexp, with_density_signed_measure_apply (integrable _ _) hi],
  { apply_instance },
  { refine vector_measure.absolutely_continuous.mk (λ j hj₁ hj₂, _),
    rw [to_ennreal_vector_measure_apply_measurable hj₁,
        trim_measurable_set_eq hle hj₁] at hj₂,
    rw [vector_measure.trim_measurable_set_eq hle hj₁,
        with_density_signed_measure_apply hfi (hle _ hj₁)],
    simp only [restrict_eq_zero.mpr hj₂, integral_zero_measure] }
end

end

/-- If `f` is measurable on a sub-σ-algebra `m`, then the condition of `f` on `n` is
equal to `f` almost everywhere. -/
lemma ae_eq_of_measurable (hle : m ≤ n) (hfm : measurable[m] f)
  (hfi : measure_theory.integrable f μ) :
  condexp hle f hfi =ᵐ[μ.trim hle] f :=
begin
  refine integrable.ae_eq_of_forall_set_integral_eq _ _ (integrable _ _)
    (hfi.trim hle hfm) (λ i hi _, _),
  rw [condexp_spec hle hfi hi, ← set_integral_trim hle hfm hi],
end

lemma ae_eq_of_measurable' (hle : m ≤ n) (hfm : measurable[m] f)
  (hfi : measure_theory.integrable f μ) :
  condexp hle f hfi =ᵐ[μ] f :=
ae_eq_of_ae_eq_trim (ae_eq_of_measurable hle hfm hfi)

-- using `ae_eq_of_ae_eq_trim` we can obtain the ae eq on `μ`

section

include hle hfi hgi

lemma add :
  condexp hle (f + g) (hfi.add hgi) =ᵐ[μ.trim hle]
  condexp hle f hfi + condexp hle g hgi :=
begin
  refine integrable.ae_eq_of_forall_set_integral_eq _ _ (integrable _ _)
    ((integrable _ _).add (integrable _ _)) (λ i hi _, _),
  rw [condexp_spec hle (hfi.add hgi) hi,
      integral_add' hfi.integrable_on hgi.integrable_on,
      ← condexp_spec hle hfi hi, ← condexp_spec hle hgi hi,
      ← integral_add'],
  { exact @integrable.integrable_on _ _ m _ _ _ _ _ (integrable hle hfi) },
  { exact @integrable.integrable_on _ _ m _ _ _ _ _ (integrable hle hgi) },
end

lemma add' :
  condexp hle (f + g) (hfi.add hgi) =ᵐ[μ]
  condexp hle f hfi + condexp hle g hgi :=
ae_eq_of_ae_eq_trim (add hle hfi hgi)

end

section

include hle hfi

variable {r : ℝ}

lemma smul :
  condexp hle (r • f) (hfi.smul r) =ᵐ[μ.trim hle] r • condexp hle f hfi :=
begin
  refine integrable.ae_eq_of_forall_set_integral_eq _ _ (integrable _ _) _ _,
  { convert ((integrable hle hfi).smul r) /- exact is slow -/ },
  { intros s hs _,
    erw [condexp_spec hle _ hs, integral_smul,
         ← condexp_spec hle hfi hs, ← integral_smul],
    refl,
    apply_instance }
end

lemma smul' :
  condexp hle (r • f) (hfi.smul r) =ᵐ[μ] r • condexp hle f hfi :=
ae_eq_of_ae_eq_trim (smul hle hfi)

end

-- maybe add `neg` and `sub` also

/- Conditioning by a random variable is the same as conditioning on the
sub-σ-algebra generated by that random variable. -/

end condexp

end measure_theory


/-

# Proposition world.

## Level 4: `apply`.

Let's do the same level again:

![diagram](https://wwwf.imperial.ac.uk/~buzzard/xena/natural_number_game_images/implies_diag.jpg)

We are given a proof $p$ of $P$ and our goal is to find a proof of $U$, or
in other words to find a path through the maze that links $P$ to $U$.
In level 3 we solved this by using `have`s to move forward, from $P$
to $Q$ to $T$ to $U$. Using the `apply` tactic we can instead construct
the path backwards, moving from $U$ to $T$ to $Q$ to $P$.

Our goal is to prove $U$. But $l:T\implies U$ is
an implication which we are assuming, so it would suffice to prove $T$.
Tell Lean this by starting the proof below with

`apply l,`

and notice that our assumptions don't change but *the goal changes*
from `⊢ U` to `⊢ T`.

Keep `apply`ing implications until your goal is `P`, and try not
to get lost! Now solve this goal
with `exact p`. Note: you will need to learn the difference between
`exact p` (which works) and `exact P` (which doesn't, because $P$ is
not a proof of $P$).
-/
/- Lemma : no-side-bar
We can solve a maze.
-/
lemma maze (P Q R S T U: Prop)
(p : P)
(h : P → Q)
(i : Q → R)
(j : Q → T)
(k : S → T)
(l : T → U)
: U :=
begin
  apply l,
  apply j,
  apply h,
  exact p,

end


import data.nat.basic
import tactic
import data.nat.prime
import number_theory.fermat4

lemma pow_nonzero {x y z: ℤ} {k: ℕ} :
x * y * z ≠ 0 → (x^k) * (y^k) * (z^k) ≠ 0
:=
begin
  intro h,
  have h4: (x * y * z)^k ≠ 0,
  {exact pow_ne_zero k h,},
  calc (x^k) * (y^k) * (z^k) = (x * y)^k * z^k  : by rw mul_pow x y k
                        ...  =  (x * y * z)^k   : by rw mul_pow (x*y) z k
                        ...  ≠ 0                : by exact h4,
end

lemma pow_assoc(x: ℤ)(m n:ℕ) : x^(m*n) = (x^m)^n
:=
begin
  exact pow_mul x m n,
end

theorem fermat_last_theorem_p :
 ∀ x y z:ℤ, ∀ n : ℕ, 2 < n ∧ nat.prime n ∧ x * y * z ≠ 0 → x^n + y^n ≠ z^n
:= sorry

theorem fermat_last_theorem_4 :
∀ x y z : ℤ, x * y * z ≠ 0 → x^4 + y^4 ≠ z^4
:=
begin
  intros x y z h,
  have h4x: x ≠ 0,
  {intro h1,
  contrapose! h,
  calc x * y * z = 0 * y * z    : by rw h1
          ...    = 0 * (y * z)  : by exact mul_assoc 0 y z
          ...    = 0            : by exact zero_mul (y*z)},
  have h4y: y ≠ 0,
  {intro h2,
  contrapose! h,
  calc x * y * z = x * 0 * z    : by rw h2
          ...    = x * (0 * z)  : by ring
          ...    = x * 0        : by rw zero_mul z
          ...    = 0            : by exact mul_zero x,},
  apply not_fermat_4 h4x h4y,
end

theorem fermat_last_theorem :
  ∀ x y z: ℤ, ∀ n: ℕ , 2 < n ∧ x * y * z ≠ 0 → x^n + y^n ≠ z^n
:=
begin
  intros x y z n h,
  have h1: (4 ∣ n) ∨ ¬ (4 ∣ n), by exact em (4 ∣ n),
  rcases h1 with ⟨ k, rfl ⟩,
  have h2: (x^k) * (y^k) * (z^k) ≠ 0, by exact pow_nonzero h.2,
  calc x^(4 * k) + y^(4 * k) = x^(k * 4) + y^(k * 4) : by rw mul_comm 4 k
       ... =(x^k)^4 + y^(k*4)                        : by rw pow_assoc x k 4
       ... = (x^k)^4 + (y^k)^4                       : by rw pow_assoc y k 4
       ... ≠ (z^k)^4                                 : by exact fermat_last_theorem_4 (x^k) (y^k) (z^k) h2
       ... = z^(k*4)                                 : by rw pow_assoc z k 4
       ... = z^(4*k)                                 : by ring_nf,

  have h3: (2 ∣ n) ∨ ¬ (2 ∣ n), by exact em (2 ∣ n),
  rcases h3 with ⟨ k, rfl ⟩,
  { have hk: 1 < k,
    {have hk1: 2 * 1 < 2 * k, by exact h.1,
     exact lt_of_mul_lt_mul_left' hk1},
    let p: ℕ := k.min_fac,
    have h4 : p ∣ k, by exact nat.min_fac_dvd k,
    cases h4 with l hl,
    have h5: p ≠ 2,
    {intro h5,
    contrapose! h1,
    calc   4 ∣ 4 * l         :  by exact dvd_mul_right 4 l
         ... = 2 * (2 * l)    :  by linarith
         ... = 2 * (p * l)    :  by rw h5
         ... = 2 * k          :  by rw hl,},
    have h6: (x^(2 * l))^p + (y^(2 * l))^p ≠ (z^(2 * l))^p,
    {have t1: x^(2 * l)*(y^(2 * l))*(z^(2 * l))≠ 0, by exact pow_nonzero h.2,
     have t2: k ≠ 1, {exact ne_comm.mp (ne_of_lt hk),},
     have t3: nat.prime p, {exact nat.min_fac_prime t2,},
     have t5: p > 2, {exact lt_iff_le_and_ne.mpr⟨nat.prime.two_le t3, ne_comm.mp h5⟩},
     have t4: p > 2 ∧ nat.prime p ∧ x^(2 * l)*(y^(2 * l))*(z^(2 * l))≠ 0, by exact ⟨t5, t3, t1⟩,
     exact fermat_last_theorem_p (x^(2 * l)) (y^(2 * l)) (z^(2 * l)) p t4,
    },
    calc x^(2 * k) + y^(2 * k) = x^(2 * (p * l)) + y^(2 * (p * l))  : by rw hl
                         ...   = x^(2 * (l * p)) + y^(2 * (l * p))  : by rw mul_comm p l
                         ...   = x^(2 * l * p) + y^(2 * l * p)      : by rw mul_assoc 2 l p
                         ...   = (x^(2 * l))^p + y^(2 * l * p)      : by rw pow_assoc x (2 * l) p
                         ...   = (x^(2 * l))^p + (y^(2 * l))^p      : by rw pow_assoc y (2 * l) p
                         ...   ≠ (z^(2 * l))^p                      : by exact h6
                         ...   = z^(2 * l * p)                      : by rw pow_assoc z (2 * l) p
                         ...   = z^(2 * (p * l))                    : by ring_nf
                         ...   = z^(2 * k)                          : by rw hl,
  },
  let p: ℕ := n.min_fac,
  have hp1 : p ≠ 2,
  begin
    contrapose! h3,
    calc 2 = p   : by exact eq.symm h3
       ... ∣ n  : by exact nat.min_fac_dvd n,
  end,
  have hp21 : n ≠ 1,
  {have hp211: n > 1, {exact gt_trans h.1 one_lt_two}, exact ne_of_gt hp211},
  have hp2 : nat.prime p, by exact nat.min_fac_prime hp21,
  have hp3 : p > 2, {exact lt_iff_le_and_ne.mpr⟨nat.prime.two_le hp2, ne_comm.mp hp1⟩},
  have hp4 : p ∣ n, by exact nat.min_fac_dvd n,
  cases hp4 with k hkp,
  have hp5: (x^k)*(y^k)*(z^k) ≠ 0, by exact pow_nonzero h.2,
  have hp6: p>2 ∧ nat.prime p ∧ (x^k)*(y^k)*(z^k) ≠ 0, by exact⟨hp3, hp2, hp5⟩,
  calc x^n + y^n = x^(p*k) + y^(p*k)  : by rw hkp
           ...   = x^(k*p) + y^(k*p)  : by ring_exp
           ...   = (x^k)^p + y^(k*p)  : by rw pow_assoc x k p
           ...   = (x^k)^p + (y^k)^p  : by rw pow_assoc y k p
           ...   ≠ (z^k)^p            : by exact fermat_last_theorem_p
                                        (x^k) (y^k) (z^k) p hp6
           ...   = z^(k*p)            : by rw pow_assoc z k p
           ...   = z^(p*k)            : by rw mul_comm p k
           ...   = z^n                : by rw hkp,
end
#lint


import data.real.basic
import tactic.suggest
import game.Completeness.level01
import game.Completeness.level02
import game.Completeness.level03
import game.Completeness.level05
import game.sup_inf.level01
import game.sup_inf.level02
import game.sup_inf.level03
import game.sup_inf.level04
import data.nat.basic
import algebra.order
namespace xena

noncomputable theory
open_locale classical

/-
# Chapter 6 : Completeness

## Level 7

Prove the the supremum of the set {3 - 1 / n : n ∈ ℕ} is 3.

Hint: Try using tauto when the goal looks obvious.

-/

example (S : set ℝ) (H : S = {r | ∃ v ∈ ℕ, r  = 3 - 1/(v + 1 : ℝ)}) : is_sup S 3 :=
begin
 rw is_sup,
 split, rw upper_bound,
 intros h j,
 rw le_iff_eq_or_lt,
 right, rw H at j,
 cases j with v hv, cases hv with f hf,
 rw hf,
 refine sub_lt.mp _,
 norm_num,
 tauto,

 intro h,
 rw upper_bound,
 cases H with t ht,
 intro j,
 apply j, tauto,

end

end xena


/-
Copyright (c) 2023 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/

import representation_theory.Rep -- representation theory via categories

/-

# Representation theory via category theory

It might have struck you as odd that we have a definition of `representation`
but not a definition of map between representations. That's because, for probably
nothing more than coincidental reasons, it was decided to set up representation
theory in Lean directly using categories. So here's how this works.

We start with a field and a group (or a ring and a monoid)
-/

variables {k : Type} [field k] {G : Type} [group G]

/-

The category of representations of G on k-vector spaces is called `Rep k G`

-/

example : Type 1 := Rep k G

/-

Wooah what is this `Type 1`: that's because the collection of all `k`-vector spaces
is *not a set*: it's too big. In set theory they use the word "class" to indicate
"collection which is too big to be a set"; in Lean we just bump up the universe
level by 1. Turns out that the full name of `Type` is `Type 0`, and if you think
set-theoretically, and think of `X : Type` as a set then you can
think of `X : Type 1` as a class. This universe bumping thing is common in category
theory.

## Category theory in Lean

Let `C` be a category.

-/

open category_theory

variables (C : Type) [category.{0} C] -- the `{0}` means "Hom(X,Y) is a set for all X and Y"!

-- Let `X` and `Y` be objects of `C`

variables (X Y : C)

-- WARNING: the morphisms between `X` and `Y` are not denoted `X → Y` with the usual arrow,
-- you have to use the category theory arrow `\hom`

example : Type := X ⟶ Y -- not the usual arrow!

example : X ⟶ X := 𝟙 X -- identity morphism

-- Let Z be another object and let φ : X ⟶ Y and ψ : Y ⟶ Z be morphisms
variables (Z : C) (φ : X ⟶ Y) (ψ : Y ⟶ Z)

-- We can compose them

example : X ⟶ Z := φ ≫ ψ -- note the order! If φ and ψ are actual functions, this would be ψ ∘ φ

-- Category axioms:

example : φ ≫ 𝟙 Y = φ := category.comp_id φ
example : 𝟙 X ≫ φ = φ := category.id_comp φ

variables (T : C) (ξ : Z ⟶ T)
example : (φ ≫ ψ) ≫ ξ = φ ≫ (ψ ≫ ξ) := category.assoc φ ψ ξ

/-

There are two notions of "being the same" in category theory; there is isomorphism,
which means what you think it means and is usually far too strong a notion, and there's
a weaker notion called equivalence. This theorem

-/

noncomputable example : Rep k G ≌ Module.{0} (monoid_algebra k G) :=
Rep.equivalence_Module_monoid_algebra

-- says that the category of representations of `G` is equivalent to the category
-- of modules for `monoid_algebra k G`. If you go "full category theory"
-- then this is the dictionary you can use to move between the two points of view.

-- Given an object `V : Rep k G`, the underlying representation of `G` is called `↥V`

example (V : Rep k G) : Type := V -- note the coercion from terms to types, `V` is a term

-- The action of `G` is given by `V.ρ`

example (g : G) (V : Rep k G) (v : V) : V := V.ρ g v

-- Now say `V` and `W` are representations in the sense of category theory

variables (V W : Rep k G)

-- A G-linear map from V to W is a morphism in the category!

variable (α : V ⟶ W) -- this is `\hom` not `→`

-- This α is some abstract morphism. To get the actual map, use α.hom

example (v : V) : W := α.hom v

example (g : G) (v : V) : α.hom (V.ρ g v) = W.ρ g (α.hom v) :=
begin
  -- this was a bit of a kerfuffle!
  have := Action.hom.comm α g,
  have this2 := linear_map.ext_iff.1 this,
  exact this2 v,
end


import data.real.basic

variables a b c : ℝ

#check mul_comm b a
#check mul_assoc c b a

-- BEGIN
example (a b c : ℝ) : (c * b) * a = b * (a * c) :=
begin
  rw mul_comm c b,
  sorry,
end

example (a b c : ℝ) : a * (b * c) = b * (a * c) :=
begin
  sorry,
end
-- END


import data.set.basic

variable α: Type*
variables A B C: set α

open set

theorem Q2a: A ∩ B = B ∩ A :=
ext $ λ x,
  ⟨λ ⟨ha, hb⟩, ⟨hb, ha⟩,
   λ ⟨hb, ha⟩, ⟨ha, hb⟩⟩

theorem Q2b: (A ∩ B) ∩ C = A ∩ (B ∩ C) :=
ext $ λ x,
  ⟨λ ⟨⟨ha, hb⟩, hc⟩, ⟨ha, ⟨hb, hc⟩⟩,
   λ ⟨ha, ⟨hb, hc⟩⟩, ⟨⟨ha, hb⟩, hc⟩⟩


/-
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
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.polynomial.coeff
import Mathlib.data.nat.with_bot
import Mathlib.PostPort

universes u v u_1

namespace Mathlib

/-!
# Theory of univariate polynomials

The definitions include
`degree`, `monic`, `leading_coeff`

Results include
- `degree_mul` : The degree of the product is the sum of degrees
- `leading_coeff_add_of_degree_eq` and `leading_coeff_add_of_degree_lt` :
    The leading_coefficient of a sum is determined by the leading coefficients and degrees
-/

namespace polynomial

/-- `degree p` is the degree of the polynomial `p`, i.e. the largest `X`-exponent in `p`.
`degree p = some n` when `p ≠ 0` and `n` is the highest power of `X` that appears in `p`, otherwise
`degree 0 = ⊥`. -/
def degree {R : Type u} [semiring R] (p : polynomial R) : with_bot ℕ :=
  finset.sup (finsupp.support p) some

theorem degree_lt_wf {R : Type u} [semiring R] :
    well_founded fun (p q : polynomial R) => degree p < degree q :=
  inv_image.wf degree (with_bot.well_founded_lt nat.lt_wf)

protected instance has_well_founded {R : Type u} [semiring R] : has_well_founded (polynomial R) :=
  has_well_founded.mk (fun (p q : polynomial R) => degree p < degree q) degree_lt_wf

/-- `nat_degree p` forces `degree p` to ℕ, by defining nat_degree 0 = 0. -/
def nat_degree {R : Type u} [semiring R] (p : polynomial R) : ℕ := option.get_or_else (degree p) 0

/-- `leading_coeff p` gives the coefficient of the highest power of `X` in `p`-/
def leading_coeff {R : Type u} [semiring R] (p : polynomial R) : R := coeff p (nat_degree p)

/-- a polynomial is `monic` if its leading coefficient is 1 -/
def monic {R : Type u} [semiring R] (p : polynomial R) := leading_coeff p = 1

theorem monic_of_subsingleton {R : Type u} [semiring R] [subsingleton R] (p : polynomial R) :
    monic p :=
  subsingleton.elim (leading_coeff p) 1

theorem monic.def {R : Type u} [semiring R] {p : polynomial R} : monic p ↔ leading_coeff p = 1 :=
  iff.rfl

protected instance monic.decidable {R : Type u} [semiring R] {p : polynomial R} [DecidableEq R] :
    Decidable (monic p) :=
  eq.mpr sorry (_inst_2 (leading_coeff p) 1)

@[simp] theorem monic.leading_coeff {R : Type u} [semiring R] {p : polynomial R} (hp : monic p) :
    leading_coeff p = 1 :=
  hp

theorem monic.coeff_nat_degree {R : Type u} [semiring R] {p : polynomial R} (hp : monic p) :
    coeff p (nat_degree p) = 1 :=
  hp

@[simp] theorem degree_zero {R : Type u} [semiring R] : degree 0 = ⊥ := rfl

@[simp] theorem nat_degree_zero {R : Type u} [semiring R] : nat_degree 0 = 0 := rfl

@[simp] theorem coeff_nat_degree {R : Type u} [semiring R] {p : polynomial R} :
    coeff p (nat_degree p) = leading_coeff p :=
  rfl

theorem degree_eq_bot {R : Type u} [semiring R] {p : polynomial R} : degree p = ⊥ ↔ p = 0 := sorry

theorem degree_of_subsingleton {R : Type u} [semiring R] {p : polynomial R} [subsingleton R] :
    degree p = ⊥ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (degree p = ⊥)) (subsingleton.elim p 0)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (degree 0 = ⊥)) degree_zero)) (Eq.refl ⊥))

theorem nat_degree_of_subsingleton {R : Type u} [semiring R] {p : polynomial R} [subsingleton R] :
    nat_degree p = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_degree p = 0)) (subsingleton.elim p 0)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_degree 0 = 0)) nat_degree_zero)) (Eq.refl 0))

theorem degree_eq_nat_degree {R : Type u} [semiring R] {p : polynomial R} (hp : p ≠ 0) :
    degree p = ↑(nat_degree p) :=
  sorry

theorem degree_eq_iff_nat_degree_eq {R : Type u} [semiring R] {p : polynomial R} {n : ℕ}
    (hp : p ≠ 0) : degree p = ↑n ↔ nat_degree p = n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (degree p = ↑n ↔ nat_degree p = n)) (degree_eq_nat_degree hp)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (↑(nat_degree p) = ↑n ↔ nat_degree p = n))
          (propext with_bot.coe_eq_coe)))
      (iff.refl (nat_degree p = n)))

theorem degree_eq_iff_nat_degree_eq_of_pos {R : Type u} [semiring R] {p : polynomial R} {n : ℕ}
    (hn : 0 < n) : degree p = ↑n ↔ nat_degree p = n :=
  sorry

theorem nat_degree_eq_of_degree_eq_some {R : Type u} [semiring R] {p : polynomial R} {n : ℕ}
    (h : degree p = ↑n) : nat_degree p = n :=
  sorry

@[simp] theorem degree_le_nat_degree {R : Type u} [semiring R] {p : polynomial R} :
    degree p ≤ ↑(nat_degree p) :=
  galois_connection.le_u_l (galois_insertion.gc with_bot.gi_get_or_else_bot) (degree p)

theorem nat_degree_eq_of_degree_eq {R : Type u} {S : Type v} [semiring R] {p : polynomial R}
    [semiring S] {q : polynomial S} (h : degree p = degree q) : nat_degree p = nat_degree q :=
  sorry

theorem le_degree_of_ne_zero {R : Type u} {n : ℕ} [semiring R] {p : polynomial R}
    (h : coeff p n ≠ 0) : ↑n ≤ degree p :=
  (fun (this : some n ≤ finset.sup (finsupp.support p) some) => this)
    (finset.le_sup (iff.mpr finsupp.mem_support_iff h))

theorem le_nat_degree_of_ne_zero {R : Type u} {n : ℕ} [semiring R] {p : polynomial R}
    (h : coeff p n ≠ 0) : n ≤ nat_degree p :=
  sorry

theorem le_nat_degree_of_mem_supp {R : Type u} [semiring R] {p : polynomial R} (a : ℕ) :
    a ∈ finsupp.support p → a ≤ nat_degree p :=
  le_nat_degree_of_ne_zero ∘ iff.mp mem_support_iff_coeff_ne_zero

theorem supp_subset_range {R : Type u} {m : ℕ} [semiring R] {p : polynomial R}
    (h : nat_degree p < m) : finsupp.support p ⊆ finset.range m :=
  fun (n : ℕ) (hn : n ∈ finsupp.support p) =>
    iff.mpr finset.mem_range (has_le.le.trans_lt (le_nat_degree_of_mem_supp n hn) h)

theorem supp_subset_range_nat_degree_succ {R : Type u} [semiring R] {p : polynomial R} :
    finsupp.support p ⊆ finset.range (nat_degree p + 1) :=
  supp_subset_range (nat.lt_succ_self (nat_degree p))

theorem degree_le_degree {R : Type u} [semiring R] {p : polynomial R} {q : polynomial R}
    (h : coeff q (nat_degree p) ≠ 0) : degree p ≤ degree q :=
  dite (p = 0)
    (fun (hp : p = 0) => eq.mpr (id (Eq._oldrec (Eq.refl (degree p ≤ degree q)) hp)) bot_le)
    fun (hp : ¬p = 0) =>
      eq.mpr (id (Eq._oldrec (Eq.refl (degree p ≤ degree q)) (degree_eq_nat_degree hp)))
        (le_degree_of_ne_zero h)

theorem degree_ne_of_nat_degree_ne {R : Type u} [semiring R] {p : polynomial R} {n : ℕ} :
    nat_degree p ≠ n → degree p ≠ ↑n :=
  sorry

theorem nat_degree_le_iff_degree_le {R : Type u} [semiring R] {p : polynomial R} {n : ℕ} :
    nat_degree p ≤ n ↔ degree p ≤ ↑n :=
  with_bot.get_or_else_bot_le_iff

theorem degree_le_of_nat_degree_le {R : Type u} [semiring R] {p : polynomial R} {n : ℕ} :
    nat_degree p ≤ n → degree p ≤ ↑n :=
  iff.mp nat_degree_le_iff_degree_le

theorem nat_degree_le_nat_degree {R : Type u} [semiring R] {p : polynomial R} {q : polynomial R}
    (hpq : degree p ≤ degree q) : nat_degree p ≤ nat_degree q :=
  galois_connection.monotone_l (galois_insertion.gc with_bot.gi_get_or_else_bot) hpq

@[simp] theorem degree_C {R : Type u} {a : R} [semiring R] (ha : a ≠ 0) : degree (coe_fn C a) = 0 :=
  (fun (this : finset.sup (ite (a = 0) ∅ (singleton 0)) some = 0) => this)
    (eq.mpr
      (id (Eq._oldrec (Eq.refl (finset.sup (ite (a = 0) ∅ (singleton 0)) some = 0)) (if_neg ha)))
      (Eq.refl (finset.sup (singleton 0) some)))

theorem degree_C_le {R : Type u} {a : R} [semiring R] : degree (coe_fn C a) ≤ 0 := sorry

theorem degree_one_le {R : Type u} [semiring R] : degree 1 ≤ 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (degree 1 ≤ 0)) (Eq.symm C_1))) degree_C_le

@[simp] theorem nat_degree_C {R : Type u} [semiring R] (a : R) : nat_degree (coe_fn C a) = 0 :=
  sorry

@[simp] theorem nat_degree_one {R : Type u} [semiring R] : nat_degree 1 = 0 := nat_degree_C 1

@[simp] theorem nat_degree_nat_cast {R : Type u} [semiring R] (n : ℕ) : nat_degree ↑n = 0 := sorry

@[simp] theorem degree_monomial {R : Type u} {a : R} [semiring R] (n : ℕ) (ha : a ≠ 0) :
    degree (coe_fn (monomial n) a) = ↑n :=
  sorry

@[simp] theorem degree_C_mul_X_pow {R : Type u} {a : R} [semiring R] (n : ℕ) (ha : a ≠ 0) :
    degree (coe_fn C a * X ^ n) = ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (degree (coe_fn C a * X ^ n) = ↑n)) (Eq.symm single_eq_C_mul_X)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (degree (coe_fn (monomial n) a) = ↑n)) (degree_monomial n ha)))
      (Eq.refl ↑n))

theorem degree_monomial_le {R : Type u} [semiring R] (n : ℕ) (a : R) :
    degree (coe_fn (monomial n) a) ≤ ↑n :=
  sorry

theorem degree_C_mul_X_pow_le {R : Type u} [semiring R] (n : ℕ) (a : R) :
    degree (coe_fn C a * X ^ n) ≤ ↑n :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (degree (coe_fn C a * X ^ n) ≤ ↑n)) (C_mul_X_pow_eq_monomial a n)))
    (degree_monomial_le n a)

@[simp] theorem nat_degree_C_mul_X_pow {R : Type u} [semiring R] (n : ℕ) (a : R) (ha : a ≠ 0) :
    nat_degree (coe_fn C a * X ^ n) = n :=
  nat_degree_eq_of_degree_eq_some (degree_C_mul_X_pow n ha)

@[simp] theorem nat_degree_C_mul_X {R : Type u} [semiring R] (a : R) (ha : a ≠ 0) :
    nat_degree (coe_fn C a * X) = 1 :=
  sorry

@[simp] theorem nat_degree_monomial {R : Type u} [semiring R] (i : ℕ) (r : R) (hr : r ≠ 0) :
    nat_degree (coe_fn (monomial i) r) = i :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (nat_degree (coe_fn (monomial i) r) = i))
        (Eq.symm (C_mul_X_pow_eq_monomial r i))))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (nat_degree (coe_fn C r * X ^ i) = i))
          (nat_degree_C_mul_X_pow i r hr)))
      (Eq.refl i))

theorem coeff_eq_zero_of_degree_lt {R : Type u} {n : ℕ} [semiring R] {p : polynomial R}
    (h : degree p < ↑n) : coeff p n = 0 :=
  iff.mp not_not (mt le_degree_of_ne_zero (not_le_of_gt h))

theorem coeff_eq_zero_of_nat_degree_lt {R : Type u} [semiring R] {p : polynomial R} {n : ℕ}
    (h : nat_degree p < n) : coeff p n = 0 :=
  sorry

@[simp] theorem coeff_nat_degree_succ_eq_zero {R : Type u} [semiring R] {p : polynomial R} :
    coeff p (nat_degree p + 1) = 0 :=
  coeff_eq_zero_of_nat_degree_lt (lt_add_one (nat_degree p))

-- We need the explicit `decidable` argument here because an exotic one shows up in a moment!

theorem ite_le_nat_degree_coeff {R : Type u} [semiring R] (p : polynomial R) (n : ℕ)
    (I : Decidable (n < 1 + nat_degree p)) : ite (n < 1 + nat_degree p) (coeff p n) 0 = coeff p n :=
  sorry

theorem as_sum_support {R : Type u} [semiring R] (p : polynomial R) :
    p = finset.sum (finsupp.support p) fun (i : ℕ) => coe_fn (monomial i) (coeff p i) :=
  Eq.symm (finsupp.sum_single p)

theorem as_sum_support_C_mul_X_pow {R : Type u} [semiring R] (p : polynomial R) :
    p = finset.sum (finsupp.support p) fun (i : ℕ) => coe_fn C (coeff p i) * X ^ i :=
  sorry

/--
We can reexpress a sum over `p.support` as a sum over `range n`,
for any `n` satisfying `p.nat_degree < n`.
-/
theorem sum_over_range' {R : Type u} {S : Type v} [semiring R] [add_comm_monoid S]
    (p : polynomial R) {f : ℕ → R → S} (h : ∀ (n : ℕ), f n 0 = 0) (n : ℕ) (w : nat_degree p < n) :
    finsupp.sum p f = finset.sum (finset.range n) fun (a : ℕ) => f a (coeff p a) :=
  finsupp.sum_of_support_subset p (supp_subset_range w) f
    fun (n_1 : ℕ) (hn : n_1 ∈ finset.range n) => h n_1

/--
We can reexpress a sum over `p.support` as a sum over `range (p.nat_degree + 1)`.
-/
theorem sum_over_range {R : Type u} {S : Type v} [semiring R] [add_comm_monoid S] (p : polynomial R)
    {f : ℕ → R → S} (h : ∀ (n : ℕ), f n 0 = 0) :
    finsupp.sum p f = finset.sum (finset.range (nat_degree p + 1)) fun (a : ℕ) => f a (coeff p a) :=
  sum_over_range' p h (nat_degree p + 1) (lt_add_one (nat_degree p))

theorem as_sum_range' {R : Type u} [semiring R] (p : polynomial R) (n : ℕ) (w : nat_degree p < n) :
    p = finset.sum (finset.range n) fun (i : ℕ) => coe_fn (monomial i) (coeff p i) :=
  Eq.trans (Eq.symm (finsupp.sum_single p))
    (sum_over_range' p (fun (n : ℕ) => finsupp.single_zero) n w)

theorem as_sum_range {R : Type u} [semiring R] (p : polynomial R) :
    p =
        finset.sum (finset.range (nat_degree p + 1))
          fun (i : ℕ) => coe_fn (monomial i) (coeff p i) :=
  Eq.trans (Eq.symm (finsupp.sum_single p)) (sum_over_range p fun (n : ℕ) => finsupp.single_zero)

theorem as_sum_range_C_mul_X_pow {R : Type u} [semiring R] (p : polynomial R) :
    p = finset.sum (finset.range (nat_degree p + 1)) fun (i : ℕ) => coe_fn C (coeff p i) * X ^ i :=
  sorry

theorem coeff_ne_zero_of_eq_degree {R : Type u} {n : ℕ} [semiring R] {p : polynomial R}
    (hn : degree p = ↑n) : coeff p n ≠ 0 :=
  fun (h : coeff p n = 0) => iff.mp finsupp.mem_support_iff (finset.mem_of_max hn) h

theorem eq_X_add_C_of_degree_le_one {R : Type u} [semiring R] {p : polynomial R}
    (h : degree p ≤ 1) : p = coe_fn C (coeff p 1) * X + coe_fn C (coeff p 0) :=
  sorry

theorem eq_X_add_C_of_degree_eq_one {R : Type u} [semiring R] {p : polynomial R}
    (h : degree p = 1) : p = coe_fn C (leading_coeff p) * X + coe_fn C (coeff p 0) :=
  sorry

theorem eq_X_add_C_of_nat_degree_le_one {R : Type u} [semiring R] {p : polynomial R}
    (h : nat_degree p ≤ 1) : p = coe_fn C (coeff p 1) * X + coe_fn C (coeff p 0) :=
  eq_X_add_C_of_degree_le_one (degree_le_of_nat_degree_le h)

theorem exists_eq_X_add_C_of_nat_degree_le_one {R : Type u} [semiring R] {p : polynomial R}
    (h : nat_degree p ≤ 1) : ∃ (a : R), ∃ (b : R), p = coe_fn C a * X + coe_fn C b :=
  Exists.intro (coeff p 1) (Exists.intro (coeff p 0) (eq_X_add_C_of_nat_degree_le_one h))

theorem degree_X_pow_le {R : Type u} [semiring R] (n : ℕ) : degree (X ^ n) ≤ ↑n := sorry

theorem degree_X_le {R : Type u} [semiring R] : degree X ≤ 1 := degree_monomial_le 1 1

theorem nat_degree_X_le {R : Type u} [semiring R] : nat_degree X ≤ 1 :=
  nat_degree_le_of_degree_le degree_X_le

theorem support_C_mul_X_pow {R : Type u} [semiring R] (c : R) (n : ℕ) :
    finsupp.support (coe_fn C c * X ^ n) ⊆ singleton n :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (finsupp.support (coe_fn C c * X ^ n) ⊆ singleton n))
        (C_mul_X_pow_eq_monomial c n)))
    finsupp.support_single_subset

theorem mem_support_C_mul_X_pow {R : Type u} [semiring R] {n : ℕ} {a : ℕ} {c : R}
    (h : a ∈ finsupp.support (coe_fn C c * X ^ n)) : a = n :=
  iff.mp finset.mem_singleton (support_C_mul_X_pow c n h)

theorem card_support_C_mul_X_pow_le_one {R : Type u} [semiring R] {c : R} {n : ℕ} :
    finset.card (finsupp.support (coe_fn C c * X ^ n)) ≤ 1 :=
  sorry

theorem card_supp_le_succ_nat_degree {R : Type u} [semiring R] (p : polynomial R) :
    finset.card (finsupp.support p) ≤ nat_degree p + 1 :=
  sorry

theorem le_degree_of_mem_supp {R : Type u} [semiring R] {p : polynomial R} (a : ℕ) :
    a ∈ finsupp.support p → ↑a ≤ degree p :=
  le_degree_of_ne_zero ∘ iff.mp mem_support_iff_coeff_ne_zero

theorem nonempty_support_iff {R : Type u} [semiring R] {p : polynomial R} :
    finset.nonempty (finsupp.support p) ↔ p ≠ 0 :=
  sorry

theorem support_C_mul_X_pow_nonzero {R : Type u} [semiring R] {c : R} {n : ℕ} (h : c ≠ 0) :
    finsupp.support (coe_fn C c * X ^ n) = singleton n :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (finsupp.support (coe_fn C c * X ^ n) = singleton n))
        (C_mul_X_pow_eq_monomial c n)))
    (finsupp.support_single_ne_zero h)

@[simp] theorem degree_one {R : Type u} [semiring R] [nontrivial R] : degree 1 = 0 :=
  degree_C ((fun (this : 1 ≠ 0) => this) (ne.symm zero_ne_one))

@[simp] theorem degree_X {R : Type u} [semiring R] [nontrivial R] : degree X = 1 :=
  degree_monomial 1 one_ne_zero

@[simp] theorem nat_degree_X {R : Type u} [semiring R] [nontrivial R] : nat_degree X = 1 :=
  nat_degree_eq_of_degree_eq_some degree_X

theorem coeff_mul_X_sub_C {R : Type u} [ring R] {p : polynomial R} {r : R} {a : ℕ} :
    coeff (p * (X - coe_fn C r)) (a + 1) = coeff p a - coeff p (a + 1) * r :=
  sorry

theorem C_eq_int_cast {R : Type u} [ring R] (n : ℤ) : coe_fn C ↑n = ↑n := ring_hom.map_int_cast C n

@[simp] theorem degree_neg {R : Type u} [ring R] (p : polynomial R) : degree (-p) = degree p :=
  sorry

@[simp] theorem nat_degree_neg {R : Type u} [ring R] (p : polynomial R) :
    nat_degree (-p) = nat_degree p :=
  sorry

@[simp] theorem nat_degree_int_cast {R : Type u} [ring R] (n : ℤ) : nat_degree ↑n = 0 := sorry

/-- The second-highest coefficient, or 0 for constants -/
def next_coeff {R : Type u} [semiring R] (p : polynomial R) : R :=
  ite (nat_degree p = 0) 0 (coeff p (nat_degree p - 1))

@[simp] theorem next_coeff_C_eq_zero {R : Type u} [semiring R] (c : R) :
    next_coeff (coe_fn C c) = 0 :=
  sorry

theorem next_coeff_of_pos_nat_degree {R : Type u} [semiring R] (p : polynomial R)
    (hp : 0 < nat_degree p) : next_coeff p = coeff p (nat_degree p - 1) :=
  sorry

theorem coeff_nat_degree_eq_zero_of_degree_lt {R : Type u} [semiring R] {p : polynomial R}
    {q : polynomial R} (h : degree p < degree q) : coeff p (nat_degree q) = 0 :=
  coeff_eq_zero_of_degree_lt (lt_of_lt_of_le h degree_le_nat_degree)

theorem ne_zero_of_degree_gt {R : Type u} [semiring R] {p : polynomial R} {n : with_bot ℕ}
    (h : n < degree p) : p ≠ 0 :=
  mt (iff.mpr degree_eq_bot) (ne.symm (ne_of_lt (lt_of_le_of_lt bot_le h)))

theorem eq_C_of_degree_le_zero {R : Type u} [semiring R] {p : polynomial R} (h : degree p ≤ 0) :
    p = coe_fn C (coeff p 0) :=
  sorry

theorem eq_C_of_degree_eq_zero {R : Type u} [semiring R] {p : polynomial R} (h : degree p = 0) :
    p = coe_fn C (coeff p 0) :=
  eq_C_of_degree_le_zero (h ▸ le_refl (degree p))

theorem degree_le_zero_iff {R : Type u} [semiring R] {p : polynomial R} :
    degree p ≤ 0 ↔ p = coe_fn C (coeff p 0) :=
  { mp := eq_C_of_degree_le_zero,
    mpr := fun (h : p = coe_fn C (coeff p 0)) => Eq.symm h ▸ degree_C_le }

theorem degree_add_le {R : Type u} [semiring R] (p : polynomial R) (q : polynomial R) :
    degree (p + q) ≤ max (degree p) (degree q) :=
  sorry

@[simp] theorem leading_coeff_zero {R : Type u} [semiring R] : leading_coeff 0 = 0 := rfl

@[simp] theorem leading_coeff_eq_zero {R : Type u} [semiring R] {p : polynomial R} :
    leading_coeff p = 0 ↔ p = 0 :=
  sorry

theorem leading_coeff_eq_zero_iff_deg_eq_bot {R : Type u} [semiring R] {p : polynomial R} :
    leading_coeff p = 0 ↔ degree p = ⊥ :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (leading_coeff p = 0 ↔ degree p = ⊥)) (propext leading_coeff_eq_zero)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (p = 0 ↔ degree p = ⊥)) (propext degree_eq_bot)))
      (iff.refl (p = 0)))

theorem nat_degree_mem_support_of_nonzero {R : Type u} [semiring R] {p : polynomial R} (H : p ≠ 0) :
    nat_degree p ∈ finsupp.support p :=
  iff.mpr (finsupp.mem_support_to_fun p (nat_degree p))
    (iff.mpr (not_congr leading_coeff_eq_zero) H)

theorem nat_degree_eq_support_max' {R : Type u} [semiring R] {p : polynomial R} (h : p ≠ 0) :
    nat_degree p = finset.max' (finsupp.support p) (iff.mpr nonempty_support_iff h) :=
  sorry

theorem nat_degree_C_mul_X_pow_le {R : Type u} [semiring R] (a : R) (n : ℕ) :
    nat_degree (coe_fn C a * X ^ n) ≤ n :=
  iff.mpr nat_degree_le_iff_degree_le (degree_C_mul_X_pow_le n a)

theorem degree_add_eq_left_of_degree_lt {R : Type u} [semiring R] {p : polynomial R}
    {q : polynomial R} (h : degree q < degree p) : degree (p + q) = degree p :=
  sorry

theorem degree_add_eq_right_of_degree_lt {R : Type u} [semiring R] {p : polynomial R}
    {q : polynomial R} (h : degree p < degree q) : degree (p + q) = degree q :=
  eq.mpr (id (Eq._oldrec (Eq.refl (degree (p + q) = degree q)) (add_comm p q)))
    (eq.mpr
      (id (Eq._oldrec (Eq.refl (degree (q + p) = degree q)) (degree_add_eq_left_of_degree_lt h)))
      (Eq.refl (degree q)))

theorem degree_add_C {R : Type u} {a : R} [semiring R] {p : polynomial R} (hp : 0 < degree p) :
    degree (p + coe_fn C a) = degree p :=
  Eq.subst (add_comm (coe_fn C a) p) degree_add_eq_right_of_degree_lt
    (lt_of_le_of_lt degree_C_le hp)

theorem degree_add_eq_of_leading_coeff_add_ne_zero {R : Type u} [semiring R] {p : polynomial R}
    {q : polynomial R} (h : leading_coeff p + leading_coeff q ≠ 0) :
    degree (p + q) = max (degree p) (degree q) :=
  sorry

theorem degree_erase_le {R : Type u} [semiring R] (p : polynomial R) (n : ℕ) :
    degree (finsupp.erase n p) ≤ degree p :=
  sorry

theorem degree_erase_lt {R : Type u} [semiring R] {p : polynomial R} (hp : p ≠ 0) :
    degree (finsupp.erase (nat_degree p) p) < degree p :=
  sorry

theorem degree_sum_le {R : Type u} [semiring R] {ι : Type u_1} (s : finset ι)
    (f : ι → polynomial R) :
    degree (finset.sum s fun (i : ι) => f i) ≤ finset.sup s fun (b : ι) => degree (f b) :=
  sorry

theorem degree_mul_le {R : Type u} [semiring R] (p : polynomial R) (q : polynomial R) :
    degree (p * q) ≤ degree p + degree q :=
  sorry

theorem degree_pow_le {R : Type u} [semiring R] (p : polynomial R) (n : ℕ) :
    degree (p ^ n) ≤ n •ℕ degree p :=
  sorry

@[simp] theorem leading_coeff_monomial {R : Type u} [semiring R] (a : R) (n : ℕ) :
    leading_coeff (coe_fn (monomial n) a) = a :=
  sorry

theorem leading_coeff_C_mul_X_pow {R : Type u} [semiring R] (a : R) (n : ℕ) :
    leading_coeff (coe_fn C a * X ^ n) = a :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (leading_coeff (coe_fn C a * X ^ n) = a)) (C_mul_X_pow_eq_monomial a n)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (leading_coeff (coe_fn (monomial n) a) = a))
          (leading_coeff_monomial a n)))
      (Eq.refl a))

@[simp] theorem leading_coeff_C {R : Type u} [semiring R] (a : R) :
    leading_coeff (coe_fn C a) = a :=
  leading_coeff_monomial a 0

@[simp] theorem leading_coeff_X_pow {R : Type u} [semiring R] (n : ℕ) : leading_coeff (X ^ n) = 1 :=
  sorry

@[simp] theorem leading_coeff_X {R : Type u} [semiring R] : leading_coeff X = 1 := sorry

@[simp] theorem monic_X_pow {R : Type u} [semiring R] (n : ℕ) : monic (X ^ n) :=
  leading_coeff_X_pow n

@[simp] theorem monic_X {R : Type u} [semiring R] : monic X := leading_coeff_X

@[simp] theorem leading_coeff_one {R : Type u} [semiring R] : leading_coeff 1 = 1 :=
  leading_coeff_C 1

@[simp] theorem monic_one {R : Type u} [semiring R] : monic 1 := leading_coeff_C 1

theorem monic.ne_zero {R : Type u_1} [semiring R] [nontrivial R] {p : polynomial R} (hp : monic p) :
    p ≠ 0 :=
  sorry

theorem monic.ne_zero_of_ne {R : Type u} [semiring R] (h : 0 ≠ 1) {p : polynomial R}
    (hp : monic p) : p ≠ 0 :=
  monic.ne_zero hp

theorem monic.ne_zero_of_polynomial_ne {R : Type u} [semiring R] {p : polynomial R}
    {q : polynomial R} {r : polynomial R} (hp : monic p) (hne : q ≠ r) : p ≠ 0 :=
  monic.ne_zero hp

theorem leading_coeff_add_of_degree_lt {R : Type u} [semiring R] {p : polynomial R}
    {q : polynomial R} (h : degree p < degree q) : leading_coeff (p + q) = leading_coeff q :=
  sorry

theorem leading_coeff_add_of_degree_eq {R : Type u} [semiring R] {p : polynomial R}
    {q : polynomial R} (h : degree p = degree q) (hlc : leading_coeff p + leading_coeff q ≠ 0) :
    leading_coeff (p + q) = leading_coeff p + leading_coeff q :=
  sorry

@[simp] theorem coeff_mul_degree_add_degree {R : Type u} [semiring R] (p : polynomial R)
    (q : polynomial R) :
    coeff (p * q) (nat_degree p + nat_degree q) = leading_coeff p * leading_coeff q :=
  sorry

theorem degree_mul' {R : Type u} [semiring R] {p : polynomial R} {q : polynomial R}
    (h : leading_coeff p * leading_coeff q ≠ 0) : degree (p * q) = degree p + degree q :=
  sorry

theorem degree_mul_monic {R : Type u} [semiring R] {p : polynomial R} {q : polynomial R}
    (hq : monic q) : degree (p * q) = degree p + degree q :=
  sorry

theorem nat_degree_mul' {R : Type u} [semiring R] {p : polynomial R} {q : polynomial R}
    (h : leading_coeff p * leading_coeff q ≠ 0) :
    nat_degree (p * q) = nat_degree p + nat_degree q :=
  sorry

theorem leading_coeff_mul' {R : Type u} [semiring R] {p : polynomial R} {q : polynomial R}
    (h : leading_coeff p * leading_coeff q ≠ 0) :
    leading_coeff (p * q) = leading_coeff p * leading_coeff q :=
  sorry

theorem leading_coeff_pow' {R : Type u} {n : ℕ} [semiring R] {p : polynomial R} :
    leading_coeff p ^ n ≠ 0 → leading_coeff (p ^ n) = leading_coeff p ^ n :=
  sorry

theorem degree_pow' {R : Type u} [semiring R] {p : polynomial R} {n : ℕ} :
    leading_coeff p ^ n ≠ 0 → degree (p ^ n) = n •ℕ degree p :=
  sorry

theorem nat_degree_pow' {R : Type u} [semiring R] {p : polynomial R} {n : ℕ}
    (h : leading_coeff p ^ n ≠ 0) : nat_degree (p ^ n) = n * nat_degree p :=
  sorry

theorem leading_coeff_mul_monic {R : Type u} [semiring R] {p : polynomial R} {q : polynomial R}
    (hq : monic q) : leading_coeff (p * q) = leading_coeff p :=
  sorry

@[simp] theorem leading_coeff_mul_X_pow {R : Type u} [semiring R] {p : polynomial R} {n : ℕ} :
    leading_coeff (p * X ^ n) = leading_coeff p :=
  leading_coeff_mul_monic (monic_X_pow n)

@[simp] theorem leading_coeff_mul_X {R : Type u} [semiring R] {p : polynomial R} :
    leading_coeff (p * X) = leading_coeff p :=
  leading_coeff_mul_monic monic_X

theorem nat_degree_mul_le {R : Type u} [semiring R] {p : polynomial R} {q : polynomial R} :
    nat_degree (p * q) ≤ nat_degree p + nat_degree q :=
  sorry

theorem subsingleton_of_monic_zero {R : Type u} [semiring R] (h : monic 0) :
    (∀ (p q : polynomial R), p = q) ∧ ∀ (a b : R), a = b :=
  sorry

theorem zero_le_degree_iff {R : Type u} [semiring R] {p : polynomial R} : 0 ≤ degree p ↔ p ≠ 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ degree p ↔ p ≠ 0)) (ne.def p 0)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ degree p ↔ ¬p = 0)) (Eq.symm (propext degree_eq_bot))))
      (option.cases_on (degree p) (of_as_true trivial) fun (val : ℕ) => of_as_true trivial))

theorem degree_nonneg_iff_ne_zero {R : Type u} [semiring R] {p : polynomial R} :
    0 ≤ degree p ↔ p ≠ 0 :=
  sorry

theorem nat_degree_eq_zero_iff_degree_le_zero {R : Type u} [semiring R] {p : polynomial R} :
    nat_degree p = 0 ↔ degree p ≤ 0 :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (nat_degree p = 0 ↔ degree p ≤ 0))
        (Eq.symm (propext nonpos_iff_eq_zero))))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (nat_degree p ≤ 0 ↔ degree p ≤ 0))
          (propext nat_degree_le_iff_degree_le)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (degree p ≤ ↑0 ↔ degree p ≤ 0)) with_bot.coe_zero))
        (iff.refl (degree p ≤ 0))))

theorem degree_le_iff_coeff_zero {R : Type u} [semiring R] (f : polynomial R) (n : with_bot ℕ) :
    degree f ≤ n ↔ ∀ (m : ℕ), n < ↑m → coeff f m = 0 :=
  sorry

theorem degree_lt_iff_coeff_zero {R : Type u} [semiring R] (f : polynomial R) (n : ℕ) :
    degree f < ↑n ↔ ∀ (m : ℕ), n ≤ m → coeff f m = 0 :=
  sorry

theorem degree_lt_degree_mul_X {R : Type u} [semiring R] {p : polynomial R} (hp : p ≠ 0) :
    degree p < degree (p * X) :=
  sorry

theorem nat_degree_pos_iff_degree_pos {R : Type u} [semiring R] {p : polynomial R} :
    0 < nat_degree p ↔ 0 < degree p :=
  lt_iff_lt_of_le_iff_le nat_degree_le_iff_degree_le

theorem eq_C_of_nat_degree_le_zero {R : Type u} [semiring R] {p : polynomial R}
    (h : nat_degree p ≤ 0) : p = coe_fn C (coeff p 0) :=
  eq_C_of_degree_le_zero (degree_le_of_nat_degree_le h)

theorem eq_C_of_nat_degree_eq_zero {R : Type u} [semiring R] {p : polynomial R}
    (h : nat_degree p = 0) : p = coe_fn C (coeff p 0) :=
  eq_C_of_nat_degree_le_zero (eq.le h)

@[simp] theorem degree_X_pow {R : Type u} [semiring R] [nontrivial R] (n : ℕ) :
    degree (X ^ n) = ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (degree (X ^ n) = ↑n)) (X_pow_eq_monomial n)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (degree (coe_fn (monomial n) 1) = ↑n))
          (degree_monomial n one_ne_zero)))
      (Eq.refl ↑n))

@[simp] theorem nat_degree_X_pow {R : Type u} [semiring R] [nontrivial R] (n : ℕ) :
    nat_degree (X ^ n) = n :=
  nat_degree_eq_of_degree_eq_some (degree_X_pow n)

theorem not_is_unit_X {R : Type u} [semiring R] [nontrivial R] : ¬is_unit X := sorry

@[simp] theorem degree_mul_X {R : Type u} [semiring R] [nontrivial R] {p : polynomial R} :
    degree (p * X) = degree p + 1 :=
  sorry

@[simp] theorem degree_mul_X_pow {R : Type u} {n : ℕ} [semiring R] [nontrivial R]
    {p : polynomial R} : degree (p * X ^ n) = degree p + ↑n :=
  sorry

theorem degree_sub_le {R : Type u} [ring R] (p : polynomial R) (q : polynomial R) :
    degree (p - q) ≤ max (degree p) (degree q) :=
  sorry

theorem degree_sub_lt {R : Type u} [ring R] {p : polynomial R} {q : polynomial R}
    (hd : degree p = degree q) (hp0 : p ≠ 0) (hlc : leading_coeff p = leading_coeff q) :
    degree (p - q) < degree p :=
  sorry

theorem nat_degree_X_sub_C_le {R : Type u} [ring R] {r : R} : nat_degree (X - coe_fn C r) ≤ 1 :=
  iff.mpr nat_degree_le_iff_degree_le
    (le_trans (degree_sub_le X (coe_fn C r))
      (max_le degree_X_le (le_trans degree_C_le (iff.mpr with_bot.coe_le_coe zero_le_one))))

theorem degree_sum_fin_lt {R : Type u} [ring R] {n : ℕ} (f : fin n → R) :
    degree (finset.sum finset.univ fun (i : fin n) => coe_fn C (f i) * X ^ ↑i) < ↑n :=
  sorry

theorem degree_sub_eq_left_of_degree_lt {R : Type u} [ring R] {p : polynomial R} {q : polynomial R}
    (h : degree q < degree p) : degree (p - q) = degree p :=
  sorry

theorem degree_sub_eq_right_of_degree_lt {R : Type u} [ring R] {p : polynomial R} {q : polynomial R}
    (h : degree p < degree q) : degree (p - q) = degree q :=
  sorry

@[simp] theorem degree_X_sub_C {R : Type u} [nontrivial R] [ring R] (a : R) :
    degree (X - coe_fn C a) = 1 :=
  sorry

@[simp] theorem nat_degree_X_sub_C {R : Type u} [nontrivial R] [ring R] (x : R) :
    nat_degree (X - coe_fn C x) = 1 :=
  nat_degree_eq_of_degree_eq_some (degree_X_sub_C x)

@[simp] theorem next_coeff_X_sub_C {R : Type u} [nontrivial R] [ring R] (c : R) :
    next_coeff (X - coe_fn C c) = -c :=
  sorry

theorem degree_X_pow_sub_C {R : Type u} [nontrivial R] [ring R] {n : ℕ} (hn : 0 < n) (a : R) :
    degree (X ^ n - coe_fn C a) = ↑n :=
  sorry

theorem X_pow_sub_C_ne_zero {R : Type u} [nontrivial R] [ring R] {n : ℕ} (hn : 0 < n) (a : R) :
    X ^ n - coe_fn C a ≠ 0 :=
  sorry

theorem X_sub_C_ne_zero {R : Type u} [nontrivial R] [ring R] (r : R) : X - coe_fn C r ≠ 0 :=
  pow_one X ▸ X_pow_sub_C_ne_zero zero_lt_one r

theorem nat_degree_X_pow_sub_C {R : Type u} [nontrivial R] [ring R] {n : ℕ} (hn : 0 < n) {r : R} :
    nat_degree (X ^ n - coe_fn C r) = n :=
  sorry

@[simp] theorem degree_mul {R : Type u} [comm_semiring R] [no_zero_divisors R] {p : polynomial R}
    {q : polynomial R} : degree (p * q) = degree p + degree q :=
  sorry

@[simp] theorem degree_pow {R : Type u} [comm_semiring R] [no_zero_divisors R] [nontrivial R]
    (p : polynomial R) (n : ℕ) : degree (p ^ n) = n •ℕ degree p :=
  sorry

@[simp] theorem leading_coeff_mul {R : Type u} [comm_semiring R] [no_zero_divisors R]
    (p : polynomial R) (q : polynomial R) :
    leading_coeff (p * q) = leading_coeff p * leading_coeff q :=
  sorry

@[simp] theorem leading_coeff_X_add_C {R : Type u} [comm_semiring R] [no_zero_divisors R]
    [nontrivial R] (a : R) (b : R) (ha : a ≠ 0) : leading_coeff (coe_fn C a * X + coe_fn C b) = a :=
  sorry

/-- `polynomial.leading_coeff` bundled as a `monoid_hom` when `R` has `no_zero_divisors`, and thus
  `leading_coeff` is multiplicative -/
def leading_coeff_hom {R : Type u} [comm_semiring R] [no_zero_divisors R] : polynomial R →* R :=
  monoid_hom.mk leading_coeff sorry leading_coeff_mul

@[simp] theorem leading_coeff_hom_apply {R : Type u} [comm_semiring R] [no_zero_divisors R]
    (p : polynomial R) : coe_fn leading_coeff_hom p = leading_coeff p :=
  rfl

@[simp] theorem leading_coeff_pow {R : Type u} [comm_semiring R] [no_zero_divisors R]
    (p : polynomial R) (n : ℕ) : leading_coeff (p ^ n) = leading_coeff p ^ n :=
  monoid_hom.map_pow leading_coeff_hom p n

end Mathlib