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

! This file was ported from Lean 3 source module data.polynomial.degree.card_pow_degree
! leanprover-community/mathlib commit 85d9f2189d9489f9983c0d01536575b0233bd305
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Order.EuclideanAbsoluteValue
import Mathbin.Data.Polynomial.FieldDivision

/-!
# Absolute value on polynomials over a finite field.

Let `Fq` be a finite field of cardinality `q`, then the map sending a polynomial `p`
to `q ^ degree p` (where `q ^ degree 0 = 0`) is an absolute value.

## Main definitions

 * `polynomial.card_pow_degree` is an absolute value on `𝔽_q[t]`, the ring of
   polynomials over a finite field of cardinality `q`, mapping a polynomial `p`
   to `q ^ degree p` (where `q ^ degree 0 = 0`)

## Main results
 * `polynomial.card_pow_degree_is_euclidean`: `card_pow_degree` respects the
   Euclidean domain structure on the ring of polynomials

-/

namespace Polynomial

variable {Fq : Type _} [Field Fq] [Fintype Fq]

open AbsoluteValue

open Classical Polynomial

#print Polynomial.cardPowDegree /-
/-- `card_pow_degree` is the absolute value on `𝔽_q[t]` sending `f` to `q ^ degree f`.

`card_pow_degree 0` is defined to be `0`. -/
noncomputable def cardPowDegree : AbsoluteValue Fq[X] ℤ :=
  have card_pos : 0 < Fintype.card Fq := Fintype.card_pos_iff.mpr inferInstance
  have pow_pos : ∀ n, 0 < (Fintype.card Fq : ℤ) ^ n := fun n =>
    pow_pos (Int.coe_nat_pos.mpr card_pos) n
  { toFun := fun p => if p = 0 then 0 else Fintype.card Fq ^ p.natDegree
    nonneg' := fun p => by
      dsimp
      split_ifs
      · rfl
      exact pow_nonneg (Int.ofNat_zero_le _) _
    eq_zero' := fun p =>
      ite_eq_left_iff.trans <|
        ⟨fun h => by
          contrapose! h
          exact ⟨h, (pow_pos _).ne'⟩, absurd⟩
    add_le' := fun p q => by
      by_cases hp : p = 0; · simp [hp]
      by_cases hq : q = 0; · simp [hq]
      by_cases hpq : p + q = 0
      · simp only [hpq, hp, hq, eq_self_iff_true, if_true, if_false]
        exact add_nonneg (pow_pos _).le (pow_pos _).le
      simp only [hpq, hp, hq, if_false]
      refine' le_trans (pow_le_pow (by linarith) (Polynomial.natDegree_add_le _ _)) _
      refine'
        le_trans (le_max_iff.mpr _)
          (max_le_add_of_nonneg (pow_nonneg (by linarith) _) (pow_nonneg (by linarith) _))
      exact (max_choice p.nat_degree q.nat_degree).imp (fun h => by rw [h]) fun h => by rw [h]
    map_mul' := fun p q => by
      by_cases hp : p = 0; · simp [hp]
      by_cases hq : q = 0; · simp [hq]
      have hpq : p * q ≠ 0 := mul_ne_zero hp hq
      simp only [hpq, hp, hq, eq_self_iff_true, if_true, if_false, Polynomial.natDegree_mul hp hq,
        pow_add] }
#align polynomial.card_pow_degree Polynomial.cardPowDegree
-/

/- warning: polynomial.card_pow_degree_apply -> Polynomial.cardPowDegree_apply is a dubious translation:
lean 3 declaration is
  forall {Fq : Type.{u1}} [_inst_1 : Field.{u1} Fq] [_inst_2 : Fintype.{u1} Fq] (p : Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))), Eq.{1} Int (coeFn.{succ u1, succ u1} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (fun (f : AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) => (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) -> Int) (AbsoluteValue.hasCoeToFun.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (Polynomial.cardPowDegree.{u1} Fq _inst_1 _inst_2) p) (ite.{1} Int (Eq.{succ u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) p (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) 0 (OfNat.mk.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) 0 (Zero.zero.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))))))) (Classical.propDecidable (Eq.{succ u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) p (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) 0 (OfNat.mk.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) 0 (Zero.zero.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1))))))))) (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Fintype.card.{u1} Fq _inst_2)) (Polynomial.natDegree.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1))) p)))
but is expected to have type
  forall {Fq : Type.{u1}} [_inst_1 : Field.{u1} Fq] [_inst_2 : Fintype.{u1} Fq] (p : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))), Eq.{1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => Int) p) (FunLike.coe.{succ u1, succ u1, 1} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (fun (f : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => (fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => Int) f) (SubadditiveHomClass.toFunLike.{u1, u1, 0} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Distrib.toAdd.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (NonUnitalNonAssocSemiring.toDistrib.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))))))) (Distrib.toAdd.{0} Int (NonUnitalNonAssocSemiring.toDistrib.{0} Int (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} Int (Semiring.toNonAssocSemiring.{0} Int (OrderedSemiring.toSemiring.{0} Int (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))))) (Preorder.toLE.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedSemiring.toPartialOrder.{0} Int (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))) (AbsoluteValue.subadditiveHomClass.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing)))))) (Polynomial.cardPowDegree.{u1} Fq _inst_1 _inst_2) p) (ite.{1} Int (Eq.{succ u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) p (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) 0 (Zero.toOfNat0.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1))))))) (Classical.propDecidable (Eq.{succ u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) p (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) 0 (Zero.toOfNat0.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))))))) (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) (HPow.hPow.{0, 0, 0} Int Nat Int Int.instHPowIntNat (Nat.cast.{0} Int instNatCastInt (Fintype.card.{u1} Fq _inst_2)) (Polynomial.natDegree.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1))) p)))
Case conversion may be inaccurate. Consider using '#align polynomial.card_pow_degree_apply Polynomial.cardPowDegree_applyₓ'. -/
theorem cardPowDegree_apply (p : Fq[X]) :
    cardPowDegree p = if p = 0 then 0 else Fintype.card Fq ^ natDegree p :=
  rfl
#align polynomial.card_pow_degree_apply Polynomial.cardPowDegree_apply

/- warning: polynomial.card_pow_degree_zero -> Polynomial.cardPowDegree_zero is a dubious translation:
lean 3 declaration is
  forall {Fq : Type.{u1}} [_inst_1 : Field.{u1} Fq] [_inst_2 : Fintype.{u1} Fq], Eq.{1} Int (coeFn.{succ u1, succ u1} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (fun (f : AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) => (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) -> Int) (AbsoluteValue.hasCoeToFun.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (Polynomial.cardPowDegree.{u1} Fq _inst_1 _inst_2) (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) 0 (OfNat.mk.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) 0 (Zero.zero.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))))))) (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))
but is expected to have type
  forall {Fq : Type.{u1}} [_inst_1 : Field.{u1} Fq] [_inst_2 : Fintype.{u1} Fq], Eq.{1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => Int) (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) 0 (Zero.toOfNat0.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1))))))) (FunLike.coe.{succ u1, succ u1, 1} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (fun (f : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => (fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => Int) f) (SubadditiveHomClass.toFunLike.{u1, u1, 0} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Distrib.toAdd.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (NonUnitalNonAssocSemiring.toDistrib.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))))))) (Distrib.toAdd.{0} Int (NonUnitalNonAssocSemiring.toDistrib.{0} Int (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} Int (Semiring.toNonAssocSemiring.{0} Int (OrderedSemiring.toSemiring.{0} Int (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))))) (Preorder.toLE.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedSemiring.toPartialOrder.{0} Int (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))) (AbsoluteValue.subadditiveHomClass.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing)))))) (Polynomial.cardPowDegree.{u1} Fq _inst_1 _inst_2) (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) 0 (Zero.toOfNat0.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1))))))) (OfNat.ofNat.{0} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => Int) (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) 0 (Zero.toOfNat0.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1))))))) 0 (instOfNatInt 0))
Case conversion may be inaccurate. Consider using '#align polynomial.card_pow_degree_zero Polynomial.cardPowDegree_zeroₓ'. -/
@[simp]
theorem cardPowDegree_zero : cardPowDegree (0 : Fq[X]) = 0 :=
  if_pos rfl
#align polynomial.card_pow_degree_zero Polynomial.cardPowDegree_zero

/- warning: polynomial.card_pow_degree_nonzero -> Polynomial.cardPowDegree_nonzero is a dubious translation:
lean 3 declaration is
  forall {Fq : Type.{u1}} [_inst_1 : Field.{u1} Fq] [_inst_2 : Fintype.{u1} Fq] (p : Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))), (Ne.{succ u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) p (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) 0 (OfNat.mk.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) 0 (Zero.zero.{u1} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))))))) -> (Eq.{1} Int (coeFn.{succ u1, succ u1} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (fun (f : AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) => (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) -> Int) (AbsoluteValue.hasCoeToFun.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (Polynomial.cardPowDegree.{u1} Fq _inst_1 _inst_2) p) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Fintype.card.{u1} Fq _inst_2)) (Polynomial.natDegree.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1))) p)))
but is expected to have type
  forall {Fq : Type.{u1}} [_inst_1 : Field.{u1} Fq] [_inst_2 : Fintype.{u1} Fq] (p : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))), (Ne.{succ u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) p (OfNat.ofNat.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) 0 (Zero.toOfNat0.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.zero.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1))))))) -> (Eq.{1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => Int) p) (FunLike.coe.{succ u1, succ u1, 1} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (fun (f : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => (fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) => Int) f) (SubadditiveHomClass.toFunLike.{u1, u1, 0} (AbsoluteValue.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Distrib.toAdd.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (NonUnitalNonAssocSemiring.toDistrib.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))))))) (Distrib.toAdd.{0} Int (NonUnitalNonAssocSemiring.toDistrib.{0} Int (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} Int (Semiring.toNonAssocSemiring.{0} Int (OrderedSemiring.toSemiring.{0} Int (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))))) (Preorder.toLE.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedSemiring.toPartialOrder.{0} Int (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))) (AbsoluteValue.subadditiveHomClass.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.semiring.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing)))))) (Polynomial.cardPowDegree.{u1} Fq _inst_1 _inst_2) p) (HPow.hPow.{0, 0, 0} Int Nat Int Int.instHPowIntNat (Nat.cast.{0} Int instNatCastInt (Fintype.card.{u1} Fq _inst_2)) (Polynomial.natDegree.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1))) p)))
Case conversion may be inaccurate. Consider using '#align polynomial.card_pow_degree_nonzero Polynomial.cardPowDegree_nonzeroₓ'. -/
@[simp]
theorem cardPowDegree_nonzero (p : Fq[X]) (hp : p ≠ 0) :
    cardPowDegree p = Fintype.card Fq ^ p.natDegree :=
  if_neg hp
#align polynomial.card_pow_degree_nonzero Polynomial.cardPowDegree_nonzero

/- warning: polynomial.card_pow_degree_is_euclidean -> Polynomial.cardPowDegree_isEuclidean is a dubious translation:
lean 3 declaration is
  forall {Fq : Type.{u1}} [_inst_1 : Field.{u1} Fq] [_inst_2 : Fintype.{u1} Fq], AbsoluteValue.IsEuclidean.{u1, 0} (Polynomial.{u1} Fq (Ring.toSemiring.{u1} Fq (DivisionRing.toRing.{u1} Fq (Field.toDivisionRing.{u1} Fq _inst_1)))) Int (Polynomial.euclideanDomain.{u1} Fq _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) (Polynomial.cardPowDegree.{u1} Fq _inst_1 _inst_2)
but is expected to have type
  forall {Fq : Type.{u1}} [_inst_1 : Field.{u1} Fq] [_inst_2 : Fintype.{u1} Fq], AbsoluteValue.IsEuclidean.{u1, 0} (Polynomial.{u1} Fq (DivisionSemiring.toSemiring.{u1} Fq (Semifield.toDivisionSemiring.{u1} Fq (Field.toSemifield.{u1} Fq _inst_1)))) Int (Polynomial.instEuclideanDomainPolynomialToSemiringToDivisionSemiringToSemifield.{u1} Fq _inst_1) (OrderedCommSemiring.toOrderedSemiring.{0} Int (StrictOrderedCommSemiring.toOrderedCommSemiring.{0} Int (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing)))) (Polynomial.cardPowDegree.{u1} Fq _inst_1 _inst_2)
Case conversion may be inaccurate. Consider using '#align polynomial.card_pow_degree_is_euclidean Polynomial.cardPowDegree_isEuclideanₓ'. -/
theorem cardPowDegree_isEuclidean : IsEuclidean (cardPowDegree : AbsoluteValue Fq[X] ℤ) :=
  have card_pos : 0 < Fintype.card Fq := Fintype.card_pos_iff.mpr inferInstance
  have pow_pos : ∀ n, 0 < (Fintype.card Fq : ℤ) ^ n := fun n =>
    pow_pos (Int.coe_nat_pos.mpr card_pos) n
  {
    map_lt_map_iff' := fun p q =>
      by
      simp only [EuclideanDomain.r, card_pow_degree_apply]
      split_ifs with hp hq hq
      · simp only [hp, hq, lt_self_iff_false]
      ·
        simp only [hp, hq, degree_zero, Ne.def, bot_lt_iff_ne_bot, degree_eq_bot, pow_pos,
          not_false_iff]
      · simp only [hp, hq, degree_zero, not_lt_bot, (pow_pos _).not_lt]
      · rw [degree_eq_nat_degree hp, degree_eq_nat_degree hq, WithBot.coe_lt_coe, pow_lt_pow_iff]
        exact_mod_cast @Fintype.one_lt_card Fq _ _ }
#align polynomial.card_pow_degree_is_euclidean Polynomial.cardPowDegree_isEuclidean

end Polynomial


/-
Copyright (c) 2018 Sander Dahmen, Johannes Hölzl, Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sander Dahmen, Johannes Hölzl, Robert Y. Lewis

"On large subsets of 𝔽ⁿ_q with no three-term arithmetic progression"
by J. S. Ellenberg and D. Gijswijt

This file proves a general result about the dimension of certain subspaces of the space of polynomials.
It corresponds to section 9 of our blueprint.
-/

import library

open mv_polynomial

section section_9

parameters {α : Type*} [discrete_field α]

parameters {n : ℕ} (V : subspace α (mv_polynomial (fin n) α)) (A : finset (fin n → α))

def zero_set : set (mv_polynomial (fin n) α) := {p ∈ V.carrier | ∀ a ∈ A, mv_polynomial.eval a p = 0}

def zero_set_subspace : subspace α (mv_polynomial (fin n) α) :=
{ carrier := zero_set,
  zero := ⟨submodule.zero _, by simp⟩,
  add := λ _ _ hx hy, ⟨submodule.add _ hx.1 hy.1, λ _ hp, by simp [hx.2 _ hp, hy.2 _ hp]⟩,
  smul := λ _ _ hp, ⟨submodule.smul _ _ hp.1, λ _ hx, by simp [hp.2 _ hx]⟩ }

def zero_set_subspace_V : subspace α V :=
{ carrier := {p | ∀ a ∈ A, mv_polynomial.eval a p = 0},
  zero := λ _ _, eval_zero,
  add := λ x y hx hy z hz, show eval z (↑x + ↑y) = 0, by simp only [eval_add, hx _ hz, hy _ hz, zero_add],
  smul := λ c x hx z hz, show eval z (c • ↑x) = 0, by simp only [mv_polynomial.smul_eval, hx _ hz, mul_zero] }

set_option class.instance_max_depth 150

def zss_equiv : zero_set_subspace ≃ₗ[α] zero_set_subspace_V :=
{ to_fun := λ p, ⟨⟨p.1, p.2.1⟩, p.2.2⟩,
  add := λ  _ _, rfl,
  smul := λ _ _, rfl,
  inv_fun := λ p, ⟨p.1, ⟨p.1.2, p.2⟩⟩,
  left_inv := λ ⟨_, _⟩, rfl,
  right_inv := λ ⟨⟨_, _⟩, _⟩, rfl }

lemma zss_v_dim_eq_zss_dim : vector_space.dim α zero_set_subspace_V = vector_space.dim α zero_set_subspace :=
zss_equiv.symm.dim_eq

set_option class.instance_max_depth 50

def Atp := {x // x ∈ A}

instance Atp_fin : fintype Atp := by dunfold Atp; apply_instance

lemma Atp_card : fintype.card Atp = A.card := fintype.subtype_card _ $ λ _, iff.refl _

def vfmap (p : V) : (Atp → α) := λ v, mv_polynomial.eval v.1 p

def vflmap : V →ₗ[α] (Atp → α) :=
{ to_fun := λ p, vfmap p,
  add := λ x y, funext $ λ z, mv_polynomial.eval_add,
  smul := λ c x, funext $ λ z, mv_polynomial.smul_eval _ _ _ }

lemma vflmap_ker : vflmap.ker = zero_set_subspace_V :=
begin
  ext,
  rw linear_map.mem_ker,
  split,
  { intros h v hv,
    change v with (⟨v, hv⟩ : Atp).val,
    apply congr_fun h },
  { intros h,
    ext v,
    apply h,
    exact v.2 }
end

lemma dim_α_A : vector_space.dim α (Atp → α) = A.card :=
by rw ←Atp_card; apply dim_fun'

lemma lemma_9_2_pre : (vector_space.dim α zero_set_subspace) + A.card ≥ (vector_space.dim α V) :=
begin
  have hrn, from dim_range_add_dim_ker vflmap,
  rw [vflmap_ker, zss_v_dim_eq_zss_dim] at hrn,
  rw [add_comm, ←hrn],
  apply add_le_add_right',
  rw ←dim_α_A,
  apply dim_submodule_le
end

lemma zero_set_subspace_dim (hv : vector_space.dim α V < cardinal.omega) :
  vector_space.dim α zero_set_subspace < cardinal.omega :=
by rw ←zss_v_dim_eq_zss_dim; exact lt_of_le_of_lt (dim_submodule_le _) hv

theorem lemma_9_2 (zss_finite_dim : vector_space.dim α zero_set_subspace < cardinal.omega) :
  (vector_space.dim α zero_set_subspace).to_nat + A.card ≥ (vector_space.dim α V).to_nat :=
suffices (vector_space.dim α zero_set_subspace + A.card).to_nat ≥ (vector_space.dim α V).to_nat,
  begin
    rw [cardinal.to_nat_add, cardinal.to_nat_coe] at this,
    { exact this },
    { exact zss_finite_dim },
    { apply cardinal.nat_lt_omega }
  end,
cardinal.to_nat_le_of_le
  (cardinal.add_lt_omega zss_finite_dim (cardinal.nat_lt_omega _))
  lemma_9_2_pre

end section_9


import meta_data
       ...folklore.real_axiom
       data.vector

namespace Hales_T_et_al_Kepler_conj

noncomputable theory

open classical nat set list real_axiom vector

-- cardinality of finite sets.
-- Surely this must exist somewhere. But where?
universe u

def set_of_list {α : Type u} : (list α) → set α
| [] y := false
| (x :: rest) y := (x = y) ∨ set_of_list rest y

def finite {α : Type u} (P : set α) :=
(∃ l, P = set_of_list l)

unfinished least : set ℕ → ℕ :=
{ description := "every subset of natural numbers has a least element" }

unfinished least_empty : least ∅ = 0 :=
{ description := "the least element of the empty set is 0 by convention" }

unfinished least_nonempty :
  ∀ (P : set ℕ),
    P ≠ ∅ → (least P ∈ P ∧ ∀ m, m ∈ P → least P ≤ m) :=
{ description := "the defining property of the least element of a subset" }

-- defaults to zero if the set is not finite:
def card {α : Type u} (P : set α) : ℕ :=
least (λ n, ∃ xs, set_of_list xs = P ∧ list.length xs = n)

/-
 Here are some Euclidean vector space definitions that
 should eventually be part of a general library
-/
local infix `^` := real_pow

def euclid_metric {n : ℕ} (u : vector ℝ n) (v : vector ℝ n) : ℝ :=
let subsquare (x : ℝ) (y : ℝ) : ℝ := (x-y)^2,
    d := to_list (map₂ subsquare u v) in real_sqrt (list.sum d)

def packing {n : ℕ} (V : set (vector ℝ n)) :=
(∀ u v, V u ∧ v ∈ V ∧ euclid_metric u v < 2 → (u = v))

-- Todo: check that open balls are used (not that it matters)
def open_ball {n : ℕ} (x0 : vector ℝ n) (r : ℝ) : (set (vector ℝ n)) :=
{ u | euclid_metric x0 u < r}

-- this is a temporary workaround (https://github.com/leanprover/lean/commit/16e7976b1a7e2ce9624ad2df363c007b70d70096)
def origin₃ : vector ℝ 3 := 0::0::0::nil--[0,0,0]

def hales_et_al_paper : document :=
{ authors := [
    {name := "Thomas Hales"},
    {name := "Mark Adams"},
    {name := "Gertrud Bauer"},
    {name := "Tat Dat Dang"},
    {name := "John Harrison"},
    {name := "Le Truong Hoang"},
    {name := "Cezary Kaliszyk"},
    {name := "Victor Magron"},
    {name := "Sean McLaughlin"},
    {name := "Tat Thang Nguyen"},
    {name := "Quang Truong Nguyen"},
    {name := "Tobias Nipkow"},
    {name := "Steven Obua"},
    {name := "Joseph Pleso"},
    {name := "Jason Rute"},
    {name := "Alexey Solovyev"},
    {name := "Thi Hoai An Ta"},
    {name := "Nam Trung Tran"},
    {name := "Thi Diep Trieu"},
    {name := "Josef Urban"},
    {name := "Ky Vu"},
    {name := "Roland Zumkelle"}
  ],
 title := "A formal proof of the Kepler Conjecture",
 doi := "/10.1017/fmp.2017.1" }

-- TODO: provide the theorem number from the paper
unfinished Kepler_conjecture :
(∀ (V : set (vector ℝ 3)), packing V →
  (∃ (c : ℝ), ∀ (r : ℝ), (r ≥ 1) ->
  (↑(card(V ∩ open_ball origin₃ r)) ≤ pi* r^3/real_sqrt(18) + c*r^2))) :=
{ description := "Proof of Kepler conjecture",
  sources := [cite.Item (cite.Document hales_et_al_paper) "Theorem X.Y"] }

def Hales_T_et_al_Kepler_conj : fabstract :=
{ contributors := [{name := "Thomas Hales"}],
  description := "This article announces the formal proof of the Kepler conjecture on dense sphere packings in a combination of the HOL Light and Isabelle/HOL proof assistants.  It represents the primary result of the now completed Flyspeck project.",
  results := [result.Proof Kepler_conjecture]
}

end Hales_T_et_al_Kepler_conj


/-
Copyright © 2020, Oracle and/or its affiliates. All rights reserved.
-/

import .relax

local attribute [instance] classical.prop_decidable

open set

namespace relax

variables (μ: probability_measure ℍ)

noncomputable
def maj_code (u: ℍ × ℍ): (ℍ × ℕ) :=
    let u1 := u.fst in
    let u2 := u.snd in
	let theta := generate_uniform_variate_simple(0,1,u1) in
	let X := generate_binomial_variate_simple(theta,u2) in
	(theta,X)

@[simp]
lemma measurable_maj_code:measurable ( λ (u: ℍ × ℍ),maj_code u) :=
begin
	unfold maj_code,
	simp,
	apply measurable.prod; simp,
	{
        apply measurable.comp,
        apply uniform_measurable,
        apply measurable.prod; simp,
        apply measurable_const,
        apply measurable.prod; simp,
        apply measurable_const,
        apply measurable_fst,
        apply measurable_id,
	},
	{
		apply measurable.comp,
        apply binomial_measurable,
        apply measurable.prod; simp,
        apply measurable.comp,
        apply uniform_measurable,
        apply measurable.prod; simp,
        apply measurable_const,
        apply measurable.prod; simp,
        apply measurable_const,
        apply measurable_fst,
        apply measurable_id,
        apply measurable_snd,
        apply measurable_id,
	}
end

axiom τ(t: ℍ): probability_measure ℍ
axiom dd: ∀ t: ℍ, τ(t) {x: ℍ | generate_uniform_variate_simple(0,1,x) = t} = 1

noncomputable
def γ(t: ℍ): probability_measure (ℍ × ℕ) :=
	map maj_code (prod.prob_measure (τ(t)) μ)

def male_selected_(t: ℍ) := {v: (ℍ × ℕ)  | v.snd = 1 ∧ v.fst = t}

@[simp]
lemma is_measurable_1: is_measurable {v: (ℍ × ℕ) | v.snd = 1} :=
begin
    have prod: {v: (ℍ × ℕ) | v.snd = 1} = set.prod {x: ℍ | true} {n: ℕ | n = 1},
    {
        rw ext_iff, intro,
        rw mem_prod_eq,
        split, intro,
        split,
        rw mem_set_of_eq at *,
        trivial,
        rw mem_set_of_eq at *,
        exact a,
        intro,
        cases a,
        rw mem_set_of_eq at *,
        exact a_right,
    },
    rw prod, clear prod,
    apply is_measurable_set_prod,
    apply is_measurable.univ,
    trivial,
end

@[simp]
lemma is_measurable_2: ∀ t, is_measurable {v: (ℍ × ℕ) | v.fst = t} :=
begin
    intro,
    have prod: {v: (ℍ × ℕ) | v.fst = t} = set.prod {t} univ,
    {
        rw ext_iff, intro,
        rw mem_prod_eq,
        split, intro,
        split,
        rw mem_set_of_eq at *,
        rw a, simp,
        rw mem_set_of_eq at *,
        trivial,
        intro,
        cases a,
        rw mem_set_of_eq at *,
        simp at a_left,
        exact a_left,
    },
    rw prod, clear prod,
    apply is_measurable_set_prod,
    swap, apply is_measurable.univ,
    apply measure_theory.is_measurable_singleton,
end

lemma test:
	∀ t, (γ μ t) {a: ℍ × ℕ | a.fst = t} = 1 :=
begin
	intro,
	dunfold γ,
	rw map_apply, simp,
	unfold maj_code, simp,
	have foo: {a: ℍ × ℍ | generate_uniform_variate_simple(0,1,a.fst) = t} = set.prod {a: ℍ | generate_uniform_variate_simple(0,1,a) = t} univ,
	{
		rw ext_iff,
		intros,
		repeat {rw mem_set_of_eq}, simp,
	},
	rw foo,
	rw prod.prob_measure_apply,
	rw dd, simp,
	simp,
	apply is_measurable.univ,
	simp,
	simp,
end

@[simp]
lemma is_measurable_3:
	∀ t, is_measurable {v: (ℍ × ℕ)  | v.snd = 1 ∧ v.fst = t} :=
begin
	intros,
	apply is_measurable.inter,
	apply is_measurable_1,
	apply is_measurable_2,
end

lemma rw_gamma_2:
	∀ t: ℍ,
	(γ μ)(t) {a: ℍ × ℕ | a.snd = 1 ∧ a.fst = t} = t :=
begin
	intros,
	dunfold γ,
	rw map_apply; simp,
	have set_rw: {a: ℍ × ℍ | (maj_code a).snd = 1 ∧ (maj_code a).fst = t} = set.prod {a: ℍ | generate_uniform_variate_simple(0,1,a) = t} {a: ℍ | generate_binomial_variate_simple(t,a) = 1},
	{
		rw ext_iff,
		intros,
		repeat {rw mem_set_of_eq}, simp,
		cases x,
		unfold maj_code, simp,

		split; intros,
		{
			cases a,
			rw a_right at *, simp at *,
			assumption,
		},
		{
			cases a,
			rw a_left at *, simp at *,
			assumption,
		},
	},
	rw set_rw, clear set_rw,
	rw prod.prob_measure_apply,

	have bin_rw:= generate_binomial_variate_simple_prop μ t,
	unfold E_bin at bin_rw,
	rw bin_rw at *,

	rw dd,
	simp,

	simp,
	simp,
end

lemma maj_:
	∀ t: ℍ,
	(γ μ)(t) (male_selected_ t) = t :=
begin
	intros,
	unfold male_selected_,
	rw rw_gamma_2,
end

noncomputable
def code (u: (ℍ × ℕ) × ℍ × ℍ): (ℍ × ℕ) × (ℍ × ℕ) :=
    let theta := u.fst.fst in
    let X := u.fst.snd in
    let u3 := u.snd.fst in
	let u4 := u.snd.snd in
	let phi := generate_uniform_variate_simple(0.8 * theta,1,u3) in
	let Y := generate_binomial_variate_simple(phi,u4) in
	((theta,X),(phi,Y))

lemma measurable_coe : measurable (coe : nnreal → real) :=
begin
	apply measure_theory.measurable_of_continuous,
	apply nnreal.continuous_coe,
end

@[simp]
lemma measurable_code:measurable ( λ (u: (ℍ × ℕ) × ℍ × ℍ),code u) :=
begin
	unfold code,
	apply measurable.prod,
	{
		apply measurable.prod; simp,
		{
			apply measurable.comp,
			apply measurable_fst,
			apply measurable_id,
			apply measurable.comp,
			apply measurable_fst,
			apply measurable_id,
			apply measurable_id,
		},
		{
			apply measurable.comp,
			apply measurable_snd,
			apply measurable_id,
			apply measurable.comp,
			apply measurable_fst,
			apply measurable_id,
			apply measurable_id,
		},
	},
	{
		apply measurable.prod; simp,
		{
        	apply measurable.comp,
        	apply uniform_measurable,
        	apply measurable.prod; simp,
        	{
				apply measure_theory.measurable_mul,
				apply measurable_const,
				apply measurable.comp,
				apply measurable_coe,
				apply measurable_fst,
				apply measurable_fst,
				apply measurable_id,
			},
        	apply measurable.prod; simp,
        	apply measurable_const,
        	apply measurable_fst,
			apply measurable_snd,
        	apply measurable_id,
		},
		{
			apply measurable.comp,
        	apply binomial_measurable,
        	apply measurable.prod; simp,
        	apply measurable.comp,
        	apply uniform_measurable,
        	apply measurable.prod; simp,
        	{
				apply measure_theory.measurable_mul,
				apply measurable_const,
				apply measurable.comp,
				apply measurable_coe,
				apply measurable_fst,
				apply measurable_fst,
				apply measurable_id,
			},
        	apply measurable.prod; simp,
        	apply measurable_const,
        	apply measurable_fst,
			apply measurable_snd,
        	apply measurable_id,
        	apply measurable_snd,
			apply measurable_snd,
        	apply measurable_id,
		},
	}
end

noncomputable
def δ(t: ℍ): probability_measure ((ℍ × ℕ) × (ℍ × ℕ)) :=
    map code (prod.prob_measure ((γ μ)(t)) (prod.prob_measure μ μ))

@[simp]
lemma is_measurable_4: ∀ t, is_measurable {v: (ℍ × ℕ) × (ℍ × ℕ) | v.fst.fst = t} :=
begin
    intro,
    have prod: {v: (ℍ × ℕ) × (ℍ × ℕ) | v.fst.fst = t} = set.prod (set.prod {t} univ) (set.prod univ univ),
    {
        rw ext_iff, intro,
        rw mem_prod_eq,
		rw mem_prod_eq,
        split,
		{
			intro,
        	split,
			split,
        	rw mem_set_of_eq at *,
        	rw a, simp,
        	rw mem_set_of_eq at *,
        	trivial,
			rw mem_set_of_eq at *,
			simp,
		},
        intro,
        cases a,
        rw mem_set_of_eq at *,
        simp at a_left,
        exact a_left,
    },
    rw prod, clear prod,
    apply is_measurable_set_prod,
	apply is_measurable_set_prod,
	apply measure_theory.is_measurable_singleton,
    apply is_measurable.univ,
	apply is_measurable_set_prod,
	apply is_measurable.univ,
	apply is_measurable.univ,
end

@[simp]
lemma is_measurable_5: is_measurable {v: (ℍ × ℕ) × (ℍ × ℕ) | v.fst.snd = 1} :=
begin
    have prod: {v: (ℍ × ℕ) × (ℍ × ℕ) | v.fst.snd = 1} = set.prod (set.prod univ {n: ℕ | n = 1}) (set.prod univ univ),
    {
        rw ext_iff, intro,
        rw mem_prod_eq,
		rw mem_prod_eq,
        split, intro,
        split,
		split,
        rw mem_set_of_eq at *,
        trivial,
        rw mem_set_of_eq at *,
        exact a,
		split,
		rw mem_set_of_eq at *,
        trivial,
		rw mem_set_of_eq at *,
        trivial,
        intro,
        cases a,
		cases a_left,
        rw mem_set_of_eq at *,
        exact a_left_right,
    },
    rw prod, clear prod,
    apply is_measurable_set_prod,
	apply is_measurable_set_prod,
    apply is_measurable.univ,
    trivial,
	apply is_measurable_set_prod,
    apply is_measurable.univ,
	apply is_measurable.univ,
end

@[simp]
lemma is_measurable_6: is_measurable {v: (ℍ × ℕ) × (ℍ × ℕ) | v.snd.snd = 1} :=
begin
    have prod: {v: (ℍ × ℕ) × (ℍ × ℕ) | v.snd.snd = 1} = set.prod (set.prod univ univ) (set.prod univ {n: ℕ | n = 1}),
    {
        rw ext_iff, intro,
        rw mem_prod_eq,
		rw mem_prod_eq,
        split, intro,
        split,
		split,
        rw mem_set_of_eq at *,
        trivial,
		rw mem_set_of_eq at *,
        trivial,
		split,
		rw mem_set_of_eq at *,
        trivial,
		rw mem_set_of_eq at *,
        exact a,
        intro,
        cases a,
		cases a_right,
        rw mem_set_of_eq at *,
        exact a_right_right,
    },
    rw prod, clear prod,
    apply is_measurable_set_prod,
	apply is_measurable_set_prod,
    apply is_measurable.univ,
	apply is_measurable.univ,
	apply is_measurable_set_prod,
    apply is_measurable.univ,
	trivial,
end

def male_selected(t: ℍ) := {v: (ℍ × ℕ) × (ℍ × ℕ) | v.fst.snd = 1 ∧ v.fst.fst = t}

@[simp]
lemma male_selected_is_measurable: ∀ t, is_measurable {v: (ℍ × ℕ) × (ℍ × ℕ) | v.fst.snd = 1 ∧ v.fst.fst = t} :=
begin
	intros,
	apply is_measurable.inter,
	apply is_measurable_5,
	apply is_measurable_4,
end

def female_selected(t: ℍ) := {v: (ℍ × ℕ) × (ℍ × ℕ) | v.snd.snd = 1 ∧ v.fst.fst = t}

@[simp]
lemma female_selected_is_measurable: ∀ t, is_measurable {v: (ℍ × ℕ) × (ℍ × ℕ) | v.snd.snd = 1 ∧ v.fst.fst = t} :=
begin
	intros,
	apply is_measurable.inter,
	apply is_measurable_6,
	apply is_measurable_4,
end

lemma rw_dive:
	∀ t: ℍ,
	∀ P: ℍ × ℕ → Prop,
	is_measurable (set_of P) →
	(δ μ) t {v: (ℍ × ℕ) × (ℍ × ℕ) | P v.fst} = (γ μ) t {v: ℍ × ℕ | P v} :=
begin
	intros,
	dunfold δ,
	dunfold γ,
	rw map_apply, simp,
	rw map_apply, simp,
	unfold code, simp,

	have set_rw: {a: (ℍ × ℕ) × ℍ × ℍ | P a.fst} = set.prod {a: ℍ × ℕ | P a} (set.prod univ univ),
	{
		rw ext_iff,
		intros,
		repeat {rw mem_set_of_eq}, simp,
	},
	rw set_rw, simp, clear set_rw,
	rw prod.prob_measure_apply, simp,
	rw map_apply, simp,

	/- Mundane measurability -/
	simp,
	apply a,
	apply a,
	apply is_measurable.univ,
	simp,
	apply a,
	simp,

	/- Interesting example -/
	have prod: {v: (ℍ × ℕ) × (ℍ × ℕ) | P (v.fst)} = set.prod {v: (ℍ × ℕ) | P(v)} univ,
    {
        rw ext_iff, intro,
        rw mem_prod_eq,
        split, intro,
        split,
        rw mem_set_of_eq at *,
        exact a_1,
        rw mem_set_of_eq at *,
        trivial,
        intro,
        cases a_1,
        rw mem_set_of_eq at *,
        exact a_1_left,
    },
    rw prod, clear prod,
    apply is_measurable_set_prod,
	exact a,
	apply is_measurable.univ,
end

lemma set_rw_1:
	∀ t: ℍ,
	set.prod {a: ℍ × ℕ | a.fst = t} (set.prod univ {a: ℍ | generate_binomial_variate_simple(4/5 * t,a) = 1})
	⊆  {a: (ℍ × ℕ) × ℍ × ℍ | (code a).snd.snd = 1 ∧ (code a).fst.fst = t} :=
begin
	intros,
	unfold set.prod,
	rw set_of_subset_set_of,
	repeat {rw mem_set_of_eq}, simp,

	intros,
	unfold code, simp,

	rw a_1 at *, simp,
	apply generate_binomial_variate_simple_prop_2 b_1.snd (4/5 *t),
	have uni_in:= generate_uniform_variate_simple_in b_1.fst (4/5 * t) 1,
	cases uni_in, assumption,
	assumption,
end

lemma principal:
	∀ t,
	4/5 * t ≤ (δ μ)(t) (female_selected t) :=
begin
	intros,
	unfold female_selected,

	have foo:= set_rw_1 t,
	have bar:= probability_measure.prob_mono (prod.prob_measure ((γ μ) t) (prod.prob_measure μ μ)) foo,
	clear foo,

	rw prod.prob_measure_apply at bar,
	rw prod.prob_measure_apply at bar,

	have bin_rw:= generate_binomial_variate_simple_prop μ (4/5 * t),
	unfold E_bin at bin_rw,
	rw bin_rw at *, clear bin_rw,

	dunfold δ,
	rw map_apply, simp,

	rw test at bar,
	simp at bar,
	exact bar,

	/- Mundane measurability -/
	simp,
	simp,
	apply is_measurable.univ,
	simp,
	simp,
	apply is_measurable_set_prod,
	apply is_measurable.univ,
	simp,
end

lemma maj:
	∀ t: ℍ,
	(δ μ)(t) (male_selected t) = t :=
begin
	intros,
	unfold male_selected,

	have rw_1 := rw_dive μ t (λ (v: ℍ × ℕ), v.snd = 1 ∧ v.fst = t),
	rw rw_1, clear rw_1,

	have prop:= maj_ μ t,
	unfold male_selected_ at prop,
	assumption,
	simp,
end

theorem final:
	∀ t: ℍ,
	(0.8:nnreal) * (δ μ)(t) (male_selected t) ≤ (δ μ)(t) (female_selected t) :=
begin
	intros,
	rw maj,
	apply principal,
end

end relax


import linear_algebra.finsupp_vector_space
import linear_algebra.free_module.finite.basic

import polyhedral_lattice.basic

/-!

# Hom(ι, Λ) for Λ a polyhedral lattice

If Λ is a polyhedral lattice and ι is a finite type, then ι → Λ is a polyhedral lattice.

## Implementation issue

We use `ι →₀ Λ` rather than `ι → Λ` to make life easier with sums.

-/
noncomputable theory

open_locale big_operators classical

variables (ι Λ : Type*)

namespace finsupp

section normed_group

variables [normed_group Λ]

instance : has_norm (ι →₀ Λ) := ⟨λ x, x.sum $ λ _, norm⟩

variables {ι Λ}

lemma norm_def (x : ι →₀ Λ) : ∥x∥ = x.sum (λ _, norm) := rfl

@[simp] lemma norm_single (i : ι) (l : Λ) : ∥single i l∥ = ∥l∥ :=
by simp only [norm_def, sum_single_index, norm_zero]

variables (ι Λ)

instance [fintype ι] : normed_group (ι →₀ Λ) :=
normed_group.of_core _ $
{ norm_eq_zero_iff := λ x,
  begin
    simp only [norm_def, sum, ← coe_nnnorm, ← nnreal.coe_sum, nnreal.coe_eq_zero, coe_zero,
      finset.sum_eq_zero_iff, nnnorm_eq_zero, mem_support_iff, ext_iff, pi.zero_apply, not_imp_self]
  end,
  triangle :=
  begin
    intros x y,
    have aux := λ z : ι →₀ Λ, @sum_fintype ι Λ _ _ _ _ z (λ i, norm) (λ i, norm_zero),
    simp only [norm_def, aux, ← finset.sum_add_distrib, add_apply],
    apply finset.sum_le_sum,
    rintro i -,
    apply norm_add_le,
  end,
  norm_neg := λ x,
  begin
    have aux := λ z : ι →₀ Λ, @sum_fintype ι Λ _ _ _ _ z (λ i, norm) (λ i, norm_zero),
    simp only [norm_def, aux, norm_neg, neg_apply]
  end }

variables {ι Λ}

lemma nnnorm_def [fintype ι] (x : ι →₀ Λ) : ∥x∥₊ = x.sum (λ _, nnnorm) :=
begin
  ext,
  simpa only [coe_nnnorm, finsupp.sum, nnreal.coe_sum] using norm_def x,
end

end normed_group

end finsupp

namespace generates_norm

open finsupp

variables [polyhedral_lattice Λ]
variables {J : Type*} (x : J → Λ)

def finsupp_generators : ι × J → (ι →₀ Λ) := λ j, single j.1 (x j.2)

variables {Λ x} [fintype J] (hx : generates_norm x)

include hx

lemma finsupp [fintype ι] : generates_norm (finsupp_generators ι Λ x) :=
begin
  dsimp only [finsupp_generators],
  intro l,
  have := λ i, hx (l i),
  choose c H1 H2 using this,
  have hl : l = ∑ i, single i (l i),
  { conv_lhs { rw [← sum_single l, finsupp.sum] },
    apply finset.sum_subset (finset.subset_univ _),
    rintro i - hi,
    rw not_mem_support_iff at hi,
    rw [hi, single_zero] },
  simp only [← single_add_hom_apply, ← add_monoid_hom.map_nsmul] at hl ⊢,
  refine ⟨λ j, c j.1 j.2, _, _⟩,
  { simp only [H1, add_monoid_hom.map_sum] at hl,
    rw [hl, ← finset.univ_product_univ, finset.sum_product] },
  { have aux := λ z : ι →₀ Λ, @sum_fintype ι Λ _ _ _ _ z (λ i, norm) (λ i, norm_zero),
    simp only [norm_def, aux, ← finset.univ_product_univ, finset.sum_product, H2,
      single_add_hom_apply, norm_single], }
end

end generates_norm

namespace finsupp

variables [polyhedral_lattice Λ]

instance {ι : Type} [fintype ι] : polyhedral_lattice (ι →₀ Λ) :=
{ finite := module.finite.of_basis (finsupp.basis (λ i, module.free.choose_basis ℤ Λ)),
  free := module.free.of_basis (finsupp.basis (λ i, module.free.choose_basis ℤ Λ)),
  polyhedral' :=
  begin
    obtain ⟨J, _instJ, x, hx⟩ := polyhedral_lattice.polyhedral' Λ, resetI,
    refine ⟨ι × J, infer_instance, λ j, single j.1 (x j.2), _⟩,
    exact hx.finsupp _
  end }

end finsupp


import data.real.basic

open nat
#check mul_zero
#check mul_succ
#check add_succ
#check add_right_comm

lemma succ_mul (a b : ℕ) : succ a * b = a * b + b :=
begin
  induction b with d hd,
  { sorry, },
  { rw mul_succ,
    rw mul_succ,
    rw hd,
    sorry, },
end


import Chap4
namespace HTPI
set_option pp.funBinderTypes true

/- Section 4.2 -/
-- 1.
theorem Exercise_4_2_9a {A B C : Type} (R : Set (A × B))
    (S : Set (B × C)) : Dom (comp S R) ⊆ Dom R := sorry

-- 2.
theorem Exercise_4_2_9b {A B C : Type} (R : Set (A × B))
    (S : Set (B × C)) : Ran R ⊆ Dom S → Dom (comp S R) = Dom R := sorry

-- 3.
--Fill in the blank to get a correct theorem and then prove the theorem
theorem Exercise_4_2_9c {A B C : Type} (R : Set (A × B))
    (S : Set (B × C)) : ___ → Ran (comp S R) = Ran S := sorry

-- 4.
theorem Exercise_4_2_12a {A B C : Type}
    (R : Set (A × B)) (S T : Set (B × C)) :
    (comp S R) \ (comp T R) ⊆ comp (S \ T) R := sorry

-- 5.
--You won't be able to complete this proof
theorem Exercise_4_2_12b {A B C : Type}
    (R : Set (A × B)) (S T : Set (B × C)) :
    comp (S \ T) R ⊆ (comp S R) \ (comp T R) := sorry

-- 6.
--You might not be able to complete this proof
theorem Exercise_4_2_14c {A B C : Type}
    (R : Set (A × B)) (S T : Set (B × C)) :
    comp (S ∩ T) R = (comp S R) ∩ (comp T R) := sorry

-- 7.
--You might not be able to complete this proof
theorem Exercise_4_2_14d {A B C : Type}
    (R : Set (A × B)) (S T : Set (B × C)) :
    comp (S ∪ T) R = (comp S R) ∪ (comp T R) := sorry

/- Section 4.3 -/
-- 1.
example :
    elementhood Int 6 { n : Int | ∃ (k : Int), n = 2 * k } := sorry

-- 2.
theorem Theorem_4_3_4_1 {A : Type} (R : BinRel A) :
    reflexive R ↔ { (x, y) : A × A | x = y } ⊆ extension R := sorry

-- 3.
theorem Theorem_4_3_4_3 {A : Type} (R : BinRel A) :
    transitive R ↔
      comp (extension R) (extension R) ⊆ extension R := sorry

-- 4.
theorem Exercise_4_3_12a {A : Type} (R : BinRel A) (h1 : reflexive R) :
    reflexive (RelFromExt (inv (extension R))) := sorry

-- 5.
theorem Exercise_4_3_12c {A : Type} (R : BinRel A) (h1 : transitive R) :
    transitive (RelFromExt (inv (extension R))) := sorry

-- 6.
theorem Exercise_4_3_18 {A : Type}
    (R S : BinRel A) (h1 : transitive R) (h2 : transitive S)
    (h3 : comp (extension S) (extension R) ⊆
      comp (extension R) (extension S)) :
    transitive (RelFromExt (comp (extension R) (extension S))) := sorry

-- 7.
--You might not be able to complete this proof
theorem Exercise_4_3_13b {A : Type}
    (R1 R2 : BinRel A) (h1 : symmetric R1) (h2 : symmetric R2) :
    symmetric (RelFromExt ((extension R1) ∪ (extension R2))) := sorry

-- 8.
--You might not be able to complete this proof
theorem Exercise_4_3_13c {A : Type}
    (R1 R2 : BinRel A) (h1 : transitive R1) (h2 : transitive R2) :
    transitive (RelFromExt ((extension R1) ∪ (extension R2))) := sorry

/- Section 4.4 -/
-- 1.
theorem Example_4_4_3_1 {A : Type} : partial_order (sub A) := sorry

-- 2.
theorem Theorem_4_4_6_1 {A : Type} (R : BinRel A) (B : Set A) (b : A)
    (h1 : partial_order R) (h2 : smallestElt R b B) :
    ∀ (c : A), smallestElt R c B → b = c := sorry

-- 3.
--If F is a set of sets, then ⋃₀ F is the lub of F in the subset ordering
theorem Theorem_4_4_11 {A : Type} (F : Set (Set A)) :
    lub (sub A) (⋃₀ F) F := sorry

-- 4.
theorem Exercise_4_4_8 {A B : Type} (R : BinRel A) (S : BinRel B)
    (T : BinRel (A × B)) (h1 : partial_order R) (h2 : partial_order S)
    (h3 : ∀ (a a' : A) (b b' : B),
      T (a, b) (a', b') ↔ R a a' ∧ S b b') :
    partial_order T := sorry

-- 5.
theorem Exercise_4_4_9_part {A B : Type} (R : BinRel A) (S : BinRel B)
    (L : BinRel (A × B)) (h1 : total_order R) (h2 : total_order S)
    (h3 : ∀ (a a' : A) (b b' : B),
      L (a, b) (a', b') ↔ R a a' ∧ (a = a' → S b b')) :
    ∀ (a a' : A) (b b' : B),
      L (a, b) (a', b') ∨ L (a', b') (a, b) := sorry

-- 6.
theorem Exercise_4_4_15a {A : Type}
    (R1 R2 : BinRel A) (B : Set A) (b : A)
    (h1 : partial_order R1) (h2 : partial_order R2)
    (h3 : extension R1 ⊆ extension R2) :
    smallestElt R1 b B → smallestElt R2 b B := sorry

-- 7.
theorem Exercise_4_4_15b {A : Type}
    (R1 R2 : BinRel A) (B : Set A) (b : A)
    (h1 : partial_order R1) (h2 : partial_order R2)
    (h3 : extension R1 ⊆ extension R2) :
    minimalElt R2 b B → minimalElt R1 b B := sorry

-- 8.
theorem Exercise_4_4_18a {A : Type}
    (R : BinRel A) (B1 B2 : Set A) (h1 : partial_order R)
    (h2 : ∀ x ∈ B1, ∃ y ∈ B2, R x y) (h3 : ∀ x ∈ B2, ∃ y ∈ B1, R x y) :
    ∀ (x : A), upperBd R x B1 ↔ upperBd R x B2 := sorry

-- 9.
theorem Exercise_4_4_22 {A : Type}
    (R : BinRel A) (B1 B2 : Set A) (x1 x2 : A)
    (h1 : lub R x1 B1) (h2 : lub R x2 B2) :
    B1 ⊆ B2 → R x1 x2 := sorry

-- 10.
theorem Exercise_4_4_24 (A : Type) (R : Set (A × A)) :
    smallestElt (sub (A × A)) (R ∪ (inv R))
    { T : Set (A × A) | R ⊆ T ∧ symmetric (RelFromExt T) } := sorry

/- Section 4.5 -/
-- 1.
lemma overlap_implies_equal_ex {A : Type}
    (F : Set (Set A)) (h : partition F) :
    ∀ X ∈ F, ∀ Y ∈ F, ∀ (x : A), x ∈ X → x ∈ Y → X = Y := sorry

-- 2.
lemma Lemma_4_5_7_ref_ex {A : Type} (F : Set (Set A)) (h : partition F):
    reflexive (EqRelFromPart F) := sorry

-- 3.
lemma Lemma_4_5_7_symm_ex {A : Type} (F : Set (Set A)) (h : partition F):
    symmetric (EqRelFromPart F) := sorry

-- 4.
lemma Lemma_4_5_7_trans_ex {A : Type} (F : Set (Set A)) (h : partition F):
    transitive (EqRelFromPart F) := sorry

-- 5.
lemma Lemma_4_5_8_ex {A : Type} (F : Set (Set A)) (h : partition F) :
    ∀ X ∈ F, ∀ x ∈ X, equivClass (EqRelFromPart F) x = X := sorry

-- 6.
lemma elt_mod_equiv_class_of_elt
    {A : Type} (R : BinRel A) (h : equiv_rel R) :
    ∀ X ∈ mod A R, ∀ x ∈ X, equivClass R x = X := sorry

-- Definitions for next three exercises:
def dot {A : Type} (F G : Set (Set A)) : Set (Set A) :=
    { Z : Set A | ¬empty Z ∧ ∃ X ∈ F, ∃ Y ∈ G, Z = X ∩ Y }

def conj {A : Type} (R S : BinRel A) (x y : A) : Prop :=
    R x y ∧ S x y

-- 7.
theorem Exercise_4_5_20a {A : Type} (R S : BinRel A)
    (h1 : equiv_rel R) (h2 : equiv_rel S) :
    equiv_rel (conj R S) := sorry

-- 8.
theorem Exercise_4_5_20b {A : Type} (R S : BinRel A)
    (h1 : equiv_rel R) (h2 : equiv_rel S) :
    ∀ (x : A), equivClass (conj R S) x =
      equivClass R x ∩ equivClass S x := sorry

-- 9.
theorem Exercise_4_5_20c {A : Type} (R S : BinRel A)
    (h1 : equiv_rel R) (h2 : equiv_rel S) :
    mod A (conj R S) = dot (mod A R) (mod A S) := sorry

-- 10.
def equiv_mod (m x y : Int) : Prop := m ∣ (x - y)

theorem Theorem_4_5_10 : ∀ (m : Int), equiv_rel (equiv_mod m) := sorry


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

! This file was ported from Lean 3 source module category_theory.monad.coequalizer
! leanprover-community/mathlib commit 3a061790136d13594ec10c7c90d202335ac5d854
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Limits.Shapes.Reflexive
import Mathbin.CategoryTheory.Limits.Shapes.SplitCoequalizer
import Mathbin.CategoryTheory.Monad.Algebra

/-!
# Special coequalizers associated to a monad

Associated to a monad `T : C ⥤ C` we have important coequalizer constructions:
Any algebra is a coequalizer (in the category of algebras) of free algebras. Furthermore, this
coequalizer is reflexive.
In `C`, this cofork diagram is a split coequalizer (in particular, it is still a coequalizer).
This split coequalizer is known as the Beck coequalizer (as it features heavily in Beck's
monadicity theorem).
-/

universe v₁ u₁

namespace CategoryTheory

namespace Monad

open Limits

variable {C : Type u₁}

variable [Category.{v₁} C]

variable {T : Monad C} (X : Algebra T)

/-!
Show that any algebra is a coequalizer of free algebras.
-/

/-- The top map in the coequalizer diagram we will construct. -/
@[simps]
def FreeCoequalizer.topMap : (Monad.free T).obj (T.obj X.A) ⟶ (Monad.free T).obj X.A :=
  (Monad.free T).map X.a
#align category_theory.monad.free_coequalizer.top_map CategoryTheory.Monad.FreeCoequalizer.topMap

/-- The bottom map in the coequalizer diagram we will construct. -/
@[simps]
def FreeCoequalizer.bottomMap : (Monad.free T).obj (T.obj X.A) ⟶ (Monad.free T).obj X.A
    where
  f := T.μ.app X.A
  h' := T.and_assoc X.A
#align category_theory.monad.free_coequalizer.bottom_map CategoryTheory.Monad.FreeCoequalizer.bottomMap

/-- The cofork map in the coequalizer diagram we will construct. -/
@[simps]
def FreeCoequalizer.π : (Monad.free T).obj X.A ⟶ X
    where
  f := X.a
  h' := X.and_assoc.symm
#align category_theory.monad.free_coequalizer.π CategoryTheory.Monad.FreeCoequalizer.π

theorem FreeCoequalizer.condition :
    FreeCoequalizer.topMap X ≫ FreeCoequalizer.π X =
      FreeCoequalizer.bottomMap X ≫ FreeCoequalizer.π X :=
  Algebra.Hom.ext _ _ X.and_assoc.symm
#align category_theory.monad.free_coequalizer.condition CategoryTheory.Monad.FreeCoequalizer.condition

instance : IsReflexivePair (FreeCoequalizer.topMap X) (FreeCoequalizer.bottomMap X) :=
  by
  apply is_reflexive_pair.mk' _ _ _
  apply (free T).map (T.η.app X.A)
  · ext
    dsimp
    rw [← functor.map_comp, X.unit, Functor.map_id]
  · ext
    apply monad.right_unit

/-- Construct the Beck cofork in the category of algebras. This cofork is reflexive as well as a
coequalizer.
-/
@[simps]
def beckAlgebraCofork : Cofork (FreeCoequalizer.topMap X) (FreeCoequalizer.bottomMap X) :=
  Cofork.ofπ _ (FreeCoequalizer.condition X)
#align category_theory.monad.beck_algebra_cofork CategoryTheory.Monad.beckAlgebraCofork

/-- The cofork constructed is a colimit. This shows that any algebra is a (reflexive) coequalizer of
free algebras.
-/
def beckAlgebraCoequalizer : IsColimit (beckAlgebraCofork X) :=
  Cofork.IsColimit.mk' _ fun s =>
    by
    have h₁ : (T : C ⥤ C).map X.a ≫ s.π.f = T.μ.app X.A ≫ s.π.f :=
      congr_arg monad.algebra.hom.f s.condition
    have h₂ : (T : C ⥤ C).map s.π.f ≫ s.X.a = T.μ.app X.A ≫ s.π.f := s.π.h
    refine' ⟨⟨T.η.app _ ≫ s.π.f, _⟩, _, _⟩
    · dsimp
      rw [functor.map_comp, category.assoc, h₂, monad.right_unit_assoc,
        show X.a ≫ _ ≫ _ = _ from T.η.naturality_assoc _ _, h₁, monad.left_unit_assoc]
    · ext
      simpa [← T.η.naturality_assoc, T.left_unit_assoc] using T.η.app ((T : C ⥤ C).obj X.A) ≫= h₁
    · intro m hm
      ext
      dsimp only
      rw [← hm]
      apply (X.unit_assoc _).symm
#align category_theory.monad.beck_algebra_coequalizer CategoryTheory.Monad.beckAlgebraCoequalizer

/-- The Beck cofork is a split coequalizer. -/
def beckSplitCoequalizer : IsSplitCoequalizer (T.map X.a) (T.μ.app _) X.a :=
  ⟨T.η.app _, T.η.app _, X.and_assoc.symm, X.Unit, T.left_unit _, (T.η.naturality _).symm⟩
#align category_theory.monad.beck_split_coequalizer CategoryTheory.Monad.beckSplitCoequalizer

/-- This is the Beck cofork. It is a split coequalizer, in particular a coequalizer. -/
@[simps pt]
def beckCofork : Cofork (T.map X.a) (T.μ.app _) :=
  (beckSplitCoequalizer X).asCofork
#align category_theory.monad.beck_cofork CategoryTheory.Monad.beckCofork

@[simp]
theorem beckCofork_π : (beckCofork X).π = X.a :=
  rfl
#align category_theory.monad.beck_cofork_π CategoryTheory.Monad.beckCofork_π

/-- The Beck cofork is a coequalizer. -/
def beckCoequalizer : IsColimit (beckCofork X) :=
  (beckSplitCoequalizer X).isCoequalizer
#align category_theory.monad.beck_coequalizer CategoryTheory.Monad.beckCoequalizer

@[simp]
theorem beckCoequalizer_desc (s : Cofork (T.toFunctor.map X.a) (T.μ.app X.A)) :
    (beckCoequalizer X).desc s = T.η.app _ ≫ s.π :=
  rfl
#align category_theory.monad.beck_coequalizer_desc CategoryTheory.Monad.beckCoequalizer_desc

end Monad

end CategoryTheory


import tactic

def reversed {α : Type} : list α → list α
| []        := []
| (x :: xs) := (reversed xs) ++ [x]

lemma reverse_preserves_length {α : Type} (l : list α) :
  l.length = (reversed l).length  :=
begin
  induction l with head tail hyp,
  {
    apply list.length_eq_zero.2,
    refl,
  },
  unfold reversed,
  rw list.length_cons,
  rw list.length_append,
  rw list.length_singleton head,
  rw hyp,
end

private lemma reverse_end {α : Type} (lizt : list α) (ende : α) :
  reversed (lizt ++ [ende]) = ende :: (reversed lizt)  :=
begin
  induction lizt with lihead litail ih,
  {
    simp [reversed],
  },
  unfold reversed,
  simp,
  unfold reversed,
  rw ih,
  refl,
end

lemma reverse_is_dual_operation {α : Type} (l : list α) :
  l = reversed (reversed l)  :=
begin
  induction l with head tail hyp;
  unfold reversed,
  {
    rw reverse_end (reversed tail) head,
    rw ← hyp,
  },
end


/-
Copyright (c) 2018 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Callum Sutton, Yury Kudryashov
Ported by: Scott Morrison

! This file was ported from Lean 3 source module algebra.hom.equiv.units.basic
! leanprover-community/mathlib commit a95b16cbade0f938fc24abd05412bde1e84bab9b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Hom.Equiv.Basic
import Mathlib.Algebra.Hom.Units

/-!
# Multiplicative and additive equivalence acting on units.
-/

variable {F α β A B M N P Q G H : Type _}

/-- A group is isomorphic to its group of units. -/
@[to_additive "An additive group is isomorphic to its group of additive units"]
def toUnits [Group G] : G ≃* Gˣ where
  toFun x := ⟨x, x⁻¹, mul_inv_self _, inv_mul_self _⟩
  invFun x := x
  left_inv _ := rfl
  right_inv _ := Units.ext rfl
  map_mul' _ _ := Units.ext rfl
#align to_units toUnits
#align to_add_units toAddUnits

@[to_additive (attr := simp)]
theorem coe_toUnits [Group G] (g : G) : (toUnits g : G) = g :=
  rfl
#align coe_to_units coe_toUnits
#align coe_to_add_units coe_toAddUnits

namespace Units

variable [Monoid M] [Monoid N] [Monoid P]

/-- A multiplicative equivalence of monoids defines a multiplicative equivalence
of their groups of units. -/
def mapEquiv (h : M ≃* N) : Mˣ ≃* Nˣ :=
  { map h.toMonoidHom with
    invFun := map h.symm.toMonoidHom,
    left_inv := fun u => ext <| h.left_inv u,
    right_inv := fun u => ext <| h.right_inv u }
#align units.map_equiv Units.mapEquiv

@[simp]
theorem mapEquiv_symm (h : M ≃* N) : (mapEquiv h).symm = mapEquiv h.symm :=
  rfl
#align units.map_equiv_symm Units.mapEquiv_symm

@[simp]
theorem coe_mapEquiv (h : M ≃* N) (x : Mˣ) : (mapEquiv h x : N) = h x :=
  rfl
#align units.coe_map_equiv Units.coe_mapEquiv

/-- Left multiplication by a unit of a monoid is a permutation of the underlying type. -/
@[to_additive (attr := simps (config := { fullyApplied := false }) apply)
  "Left addition of an additive unit is a permutation of the underlying type."]
def mulLeft (u : Mˣ) : Equiv.Perm M where
  toFun x := u * x
  invFun x := u⁻¹ * x
  left_inv := u.inv_mul_cancel_left
  right_inv := u.mul_inv_cancel_left
#align units.mul_left Units.mulLeft
#align add_units.add_left AddUnits.addLeft
#align units.mul_left_apply Units.mulLeft_apply
#align add_units.add_left_apply AddUnits.addLeft_apply

@[to_additive (attr := simp)]
theorem mulLeft_symm (u : Mˣ) : u.mulLeft.symm = u⁻¹.mulLeft :=
  Equiv.ext fun _ => rfl
#align units.mul_left_symm Units.mulLeft_symm
#align add_units.add_left_symm AddUnits.addLeft_symm

@[to_additive]
theorem mulLeft_bijective (a : Mˣ) : Function.Bijective ((a * ·) : M → M) :=
  (mulLeft a).bijective
#align units.mul_left_bijective Units.mulLeft_bijective
#align add_units.add_left_bijective AddUnits.addLeft_bijective

/-- Right multiplication by a unit of a monoid is a permutation of the underlying type. -/
@[to_additive (attr := simps (config := { fullyApplied := false }) apply)
"Right addition of an additive unit is a permutation of the underlying type."]
def mulRight (u : Mˣ) : Equiv.Perm M where
  toFun x := x * u
  invFun x := x * ↑u⁻¹
  left_inv x := mul_inv_cancel_right x u
  right_inv x := inv_mul_cancel_right x u
#align units.mul_right Units.mulRight
#align add_units.add_right AddUnits.addRight
#align units.mul_right_apply Units.mulRight_apply
#align add_units.add_right_apply AddUnits.addRight_apply

@[to_additive (attr := simp)]
theorem mulRight_symm (u : Mˣ) : u.mulRight.symm = u⁻¹.mulRight :=
  Equiv.ext fun _ => rfl
#align units.mul_right_symm Units.mulRight_symm
#align add_units.add_right_symm AddUnits.addRight_symm

@[to_additive]
theorem mulRight_bijective (a : Mˣ) : Function.Bijective ((· * a) : M → M) :=
  (mulRight a).bijective
#align units.mul_right_bijective Units.mulRight_bijective
#align add_units.add_right_bijective AddUnits.addRight_bijective

end Units

namespace Equiv

section Group

variable [Group G]

/-- Left multiplication in a `Group` is a permutation of the underlying type. -/
@[to_additive "Left addition in an `AddGroup` is a permutation of the underlying type."]
protected def mulLeft (a : G) : Perm G :=
  (toUnits a).mulLeft
#align equiv.mul_left Equiv.mulLeft
#align equiv.add_left Equiv.addLeft

@[to_additive (attr := simp)]
theorem coe_mulLeft (a : G) : ⇑(Equiv.mulLeft a) = (· * ·) a :=
  rfl
#align equiv.coe_mul_left Equiv.coe_mulLeft
#align equiv.coe_add_left Equiv.coe_addLeft

-- Porting note: we don't put `@[simp]` on the additive version;
-- mysteriously simp can already prove that one (although not the multiplicative one)!
/-- Extra simp lemma that `dsimp` can use. `simp` will never use this. -/
@[to_additive "Extra simp lemma that `dsimp` can use. `simp` will never use this.",
  simp, nolint simpNF]

@[to_additive (attr := simp)]
theorem mulLeft_symm (a : G) : (Equiv.mulLeft a).symm = Equiv.mulLeft a⁻¹ :=
  ext fun _ => rfl
#align equiv.mul_left_symm Equiv.mulLeft_symm
#align equiv.add_left_symm Equiv.addLeft_symm

@[to_additive]
theorem _root_.Group.mulLeft_bijective (a : G) : Function.Bijective (a * ·) :=
  (Equiv.mulLeft a).bijective
#align group.mul_left_bijective Group.mulLeft_bijective
#align add_group.add_left_bijective AddGroup.addLeft_bijective

/-- Right multiplication in a `Group` is a permutation of the underlying type. -/
@[to_additive "Right addition in an `AddGroup` is a permutation of the underlying type."]
protected def mulRight (a : G) : Perm G :=
  (toUnits a).mulRight
#align equiv.mul_right Equiv.mulRight
#align equiv.add_right Equiv.addRight

@[to_additive (attr := simp)]
theorem coe_mulRight (a : G) : ⇑(Equiv.mulRight a) = fun x => x * a :=
  rfl
#align equiv.coe_mul_right Equiv.coe_mulRight
#align equiv.coe_add_right Equiv.coe_addRight

@[to_additive (attr := simp)]
theorem mulRight_symm (a : G) : (Equiv.mulRight a).symm = Equiv.mulRight a⁻¹ :=
  ext fun _ => rfl
#align equiv.mul_right_symm Equiv.mulRight_symm
#align equiv.add_right_symm Equiv.addRight_symm

/-- Extra simp lemma that `dsimp` can use. `simp` will never use this. -/
@[to_additive "Extra simp lemma that `dsimp` can use. `simp` will never use this.",
  simp, nolint simpNF]
theorem mulRight_symm_apply (a : G) : ((Equiv.mulRight a).symm : G → G) = fun x => x * a⁻¹ :=
  rfl
#align equiv.mul_right_symm_apply Equiv.mulRight_symm_apply
#align equiv.add_right_symm_apply Equiv.addRight_symm_apply

@[to_additive]
theorem _root_.Group.mulRight_bijective (a : G) : Function.Bijective (· * a) :=
  (Equiv.mulRight a).bijective
#align group.mul_right_bijective Group.mulRight_bijective
#align add_group.add_right_bijective AddGroup.addRight_bijective

/-- A version of `Equiv.mulLeft a b⁻¹` that is defeq to `a / b`. -/
@[to_additive (attr := simps) " A version of `Equiv.addLeft a (-b)` that is defeq to `a - b`. "]
protected def divLeft (a : G) : G ≃ G where
  toFun b := a / b
  invFun b := b⁻¹ * a
  left_inv b := by simp [div_eq_mul_inv]
  right_inv b := by simp [div_eq_mul_inv]
#align equiv.div_left Equiv.divLeft
#align equiv.sub_left Equiv.subLeft
#align equiv.div_left_apply Equiv.divLeft_apply
#align equiv.div_left_symm_apply Equiv.divLeft_symm_apply
#align equiv.sub_left_apply Equiv.subLeft_apply
#align equiv.sub_left_symm_apply Equiv.subLeft_symm_apply

@[to_additive]
theorem divLeft_eq_inv_trans_mulLeft (a : G) :
    Equiv.divLeft a = (Equiv.inv G).trans (Equiv.mulLeft a) :=
  ext fun _ => div_eq_mul_inv _ _
#align equiv.div_left_eq_inv_trans_mul_left Equiv.divLeft_eq_inv_trans_mulLeft
#align equiv.sub_left_eq_neg_trans_add_left Equiv.subLeft_eq_neg_trans_addLeft

/-- A version of `Equiv.mulRight a⁻¹ b` that is defeq to `b / a`. -/
@[to_additive (attr := simps) " A version of `Equiv.addRight (-a) b` that is defeq to `b - a`. "]
protected def divRight (a : G) : G ≃
      G where
  toFun b := b / a
  invFun b := b * a
  left_inv b := by simp [div_eq_mul_inv]
  right_inv b := by simp [div_eq_mul_inv]
#align equiv.div_right Equiv.divRight
#align equiv.sub_right Equiv.subRight
#align equiv.div_right_symm_apply Equiv.divRight_symm_apply
#align equiv.div_right_apply Equiv.divRight_apply
#align equiv.sub_right_symm_apply Equiv.subRight_symm_apply
#align equiv.sub_right_apply Equiv.subRight_apply

@[to_additive]
theorem divRight_eq_mulRight_inv (a : G) : Equiv.divRight a = Equiv.mulRight a⁻¹ :=
  ext fun _ => div_eq_mul_inv _ _
#align equiv.div_right_eq_mul_right_inv Equiv.divRight_eq_mulRight_inv
#align equiv.sub_right_eq_add_right_neg Equiv.subRight_eq_addRight_neg

end Group

end Equiv

-- Porting note: we don't put `@[simp]` on the additive version;
-- mysteriously simp can already prove that one (although not the multiplicative one)!
-- porting note: `@[simps apply]` removed because right now it's generating lemmas which
-- aren't in simp normal form (they contain a `toFun`)
/-- In a `DivisionCommMonoid`, `Equiv.inv` is a `MulEquiv`. There is a variant of this
`MulEquiv.inv' G : G ≃* Gᵐᵒᵖ` for the non-commutative case. -/
@[to_additive (attr := simps apply)
  "When the `AddGroup` is commutative, `Equiv.neg` is an `AddEquiv`."]
def MulEquiv.inv (G : Type _) [DivisionCommMonoid G] : G ≃* G :=
  { Equiv.inv G with toFun := Inv.inv, invFun := Inv.inv, map_mul' := mul_inv }
#align mul_equiv.inv MulEquiv.inv
#align add_equiv.neg AddEquiv.neg
#align mul_equiv.inv_apply MulEquiv.inv_apply
#align add_equiv.neg_apply AddEquiv.neg_apply

@[to_additive (attr := simp)]
theorem MulEquiv.inv_symm (G : Type _) [DivisionCommMonoid G] :
    (MulEquiv.inv G).symm = MulEquiv.inv G :=
  rfl
#align mul_equiv.inv_symm MulEquiv.inv_symm
-- porting note: no `add_equiv.neg_symm` in `mathlib3`


/-
Copyright (c) 2018 Andreas Swerdlow. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Andreas Swerdlow, Kenny Lau
-/
import data.equiv.ring
import algebra.ring.opposite

/-!
# Ring involutions

This file defines a ring involution as a structure extending `R ≃+* Rᵐᵒᵖ`,
with the additional fact `f.involution : (f (f x).unop).unop = x`.

## Notations

We provide a coercion to a function `R → Rᵐᵒᵖ`.

## References

* <https://en.wikipedia.org/wiki/Involution_(mathematics)#Ring_theory>

## Tags

Ring involution
-/

variables (R : Type*)

/-- A ring involution -/
structure ring_invo [semiring R] extends R ≃+* Rᵐᵒᵖ :=
(involution' : ∀ x, (to_fun (to_fun x).unop).unop = x)

namespace ring_invo
variables {R} [semiring R]

/-- Construct a ring involution from a ring homomorphism. -/
def mk' (f : R →+* Rᵐᵒᵖ) (involution : ∀ r, (f (f r).unop).unop = r) :
  ring_invo R :=
{ inv_fun := λ r, (f r.unop).unop,
  left_inv := λ r, involution r,
  right_inv := λ r, mul_opposite.unop_injective $ involution _,
  involution' := involution,
  .. f }

instance : has_coe_to_fun (ring_invo R) (λ _, R → Rᵐᵒᵖ) := ⟨λ f, f.to_ring_equiv.to_fun⟩

@[simp]
lemma to_fun_eq_coe (f : ring_invo R) : f.to_fun = f := rfl

@[simp]
lemma involution (f : ring_invo R) (x : R) : (f (f x).unop).unop = x :=
f.involution' x

instance has_coe_to_ring_equiv : has_coe (ring_invo R) (R ≃+* Rᵐᵒᵖ) :=
⟨ring_invo.to_ring_equiv⟩

@[norm_cast] lemma coe_ring_equiv (f : ring_invo R) (a : R) :
  (f : R ≃+* Rᵐᵒᵖ) a = f a := rfl

@[simp] lemma map_eq_zero_iff (f : ring_invo R) {x : R} : f x = 0 ↔ x = 0 :=
f.to_ring_equiv.map_eq_zero_iff

end ring_invo

open ring_invo

section comm_ring
variables [comm_ring R]

/-- The identity function of a `comm_ring` is a ring involution. -/
protected def ring_invo.id : ring_invo R :=
{ involution' := λ r, rfl,
  ..(ring_equiv.to_opposite R) }

instance : inhabited (ring_invo R) := ⟨ring_invo.id _⟩

end comm_ring