import tactic
import binary
import hamming

/-!
# Syndrome Decoding for the Hamming(7,4) code.

This file contains a formalization of the Hamming(7,4) code.
In particular, we formalize encoding, decoding, and error correction procedures.
We use a structure `BECC` (Binary Error Correcting Code) to guide this formalization.

The reader may be intersete in using `#eval` commands to try out this code.
Or see the releated `hamcode_widget.lean` file.
-/

structure BECC (n m d: ℕ) :=
  (encode : BW m → BW n)
  (decode : BW n → BW m)
  (error_count : BW n → BW n → ℕ)
  (error_correct : BW n → BW n)
  (error_correcting_code :
    ∀ (msg : BW m) (received : BW n),
      error_count (encode msg) received ≤ (d - 1)/2 →
        decode (error_correct received) = msg)

namespace hamming_code

open B BW

/-- The generator matrix. -/
def G := ᴹ[
  ᴮ[I,I,O,I],
  ᴮ[I,O,I,I],
  ᴮ[I,O,O,O],
  ᴮ[O,I,I,I],
  ᴮ[O,I,O,O],
  ᴮ[O,O,I,O],
  ᴮ[O,O,O,I]
]

/-- The encoding procedure. -/
def encode (u: BW 4) : BW 7 := G × u

@[simp]
def encOOOO : encode ᴮ[O,O,O,O] = ᴮ[O,O,O,O,O,O,O] := rfl
@[simp]
def encOOOI : encode ᴮ[O,O,O,I] = ᴮ[I,I,O,I,O,O,I] := rfl
@[simp]
def encOOIO : encode ᴮ[O,O,I,O] = ᴮ[O,I,O,I,O,I,O] := rfl
@[simp]
def encOOII : encode ᴮ[O,O,I,I] = ᴮ[I,O,O,O,O,I,I] := rfl
@[simp]
def encOIOO : encode ᴮ[O,I,O,O] = ᴮ[I,O,O,I,I,O,O] := rfl
@[simp]
def encOIOI : encode ᴮ[O,I,O,I] = ᴮ[O,I,O,O,I,O,I] := rfl
@[simp]
def encOIIO : encode ᴮ[O,I,I,O] = ᴮ[I,I,O,O,I,I,O] := rfl
@[simp]
def encOIII : encode ᴮ[O,I,I,I] = ᴮ[O,O,O,I,I,I,I] := rfl
@[simp]
def encIOOO : encode ᴮ[I,O,O,O] = ᴮ[I,I,I,O,O,O,O] := rfl
@[simp]
def encIOOI : encode ᴮ[I,O,O,I] = ᴮ[O,O,I,I,O,O,I] := rfl
@[simp]
def encIOIO : encode ᴮ[I,O,I,O] = ᴮ[I,O,I,I,O,I,O] := rfl
@[simp]
def encIOII : encode ᴮ[I,O,I,I] = ᴮ[O,I,I,O,O,I,I] := rfl
@[simp]
def encIIOO : encode ᴮ[I,I,O,O] = ᴮ[O,I,I,I,I,O,O] := rfl
@[simp]
def encIIOI : encode ᴮ[I,I,O,I] = ᴮ[I,O,I,O,I,O,I] := rfl
@[simp]
def encIIIO : encode ᴮ[I,I,I,O] = ᴮ[O,O,I,O,I,I,O] := rfl
@[simp]
def encIIII : encode ᴮ[I,I,I,I] = ᴮ[I,I,I,I,I,I,I] := rfl

/-- The decoding matrix. -/
def R := ᴹ[
  ᴮ[O,O,I,O,O,O,O],
  ᴮ[O,O,O,O,I,O,O],
  ᴮ[O,O,O,O,O,I,O],
  ᴮ[O,O,O,O,O,O,I]
]

/-- The decoding procedure. Assumes that errors have been corrected. -/
def decode (v: BW 7) : BW 4 := R × v

@[simp]
lemma decode_inverse_of_encode (msg : BW 4) : decode (encode msg) = msg :=
begin
  rcases msg with _|⟨_,a,_|⟨_,b,_|⟨_,c,_|⟨_,d,⟨nil⟩⟩⟩⟩⟩,
  cases a; cases b; cases c; cases d; refl,
end

/-- The parity-check matrix. -/
def H := ᴹ[
  ᴮ[I,O,I,O,I,O,I],
  ᴮ[O,I,I,O,O,I,I],
  ᴮ[O,O,O,I,I,I,I]
]

/--
Given a syndrome resulting from matrix multiplication with the parity-check matrix,
returns the error vector corresponding to that syndrome.
Note: due to rhs multiplication,
the reverse of a syndrome is the binary representation of the error location.
-/
def syndrome_to_error_vector : BW 3 → BW 7
| ᴮ[O,O,O] := ᴮ[O,O,O,O,O,O,O]
| ᴮ[I,O,O] := ᴮ[I,O,O,O,O,O,O]
| ᴮ[O,I,O] := ᴮ[O,I,O,O,O,O,O]
| ᴮ[I,I,O] := ᴮ[O,O,I,O,O,O,O]
| ᴮ[O,O,I] := ᴮ[O,O,O,I,O,O,O]
| ᴮ[I,O,I] := ᴮ[O,O,O,O,I,O,O]
| ᴮ[O,I,I] := ᴮ[O,O,O,O,O,I,O]
| ᴮ[I,I,I] := ᴮ[O,O,O,O,O,O,I]

/-- The error correction procedure -/
def error_correct (received : BW 7) : BW 7 :=
received - syndrome_to_error_vector (H × received)

/-- A tactic for finding the location of an error in a binary word. -/
meta def find_error : tactic unit :=
`[
  simp at *,
  cases received with _ hd tl, cases hd;
    {simp at h, rw h.symm, refl}
    <|>
    {cases tl with _ hd tl, cases hd;
      {simp at h, rw h.symm, refl}
      <|>
      {cases tl with _ hd tl, cases hd;
        {simp at h, rw h.symm, refl}
        <|>
        {cases tl with _ hd tl, cases hd;
          {simp at h, rw h.symm, refl}
          <|>
          {cases tl with _ hd tl, cases hd;
            {simp at h, rw h.symm, refl}
            <|>
            {cases tl with _ hd tl, cases hd;
              {simp at h, rw h.symm, refl}
              <|>
              {rcases tl with _ | ⟨_, hd, ⟨nil⟩⟩, cases hd; refl,},
            }
          }
        }
      }
    }
]

/--
The error correcting procedure defined above can correct single-bit errors.
-/
lemma single_error_correctable (msg : BW 4) (received : BW 7) :
  d(encode msg, received) = 1 → error_correct received = encode msg :=
begin
  intro h,
  rcases msg with _|⟨_,a,_|⟨_,b,_|⟨_,c,_|⟨_,d,⟨nil⟩⟩⟩⟩⟩,
  cases a; cases b; cases c; cases d; find_error,
end

lemma no_encoding_errors (msg : BW 4) : error_correct (encode msg) = encode msg :=
begin
  rw encode,
  rcases msg with _|⟨_,a,_|⟨_,b,_|⟨_,c,_|⟨_,d,⟨nil⟩⟩⟩⟩⟩,
  cases a; cases b; cases c; cases d; refl,
end

theorem one_error_correcting_code (msg : BW 4) (received : BW 7) :
  d(encode msg, received) ≤ 1 → decode (error_correct received) = msg :=
begin
  intro h,
  have h₁ : d(encode msg, received) = 0 ∨ d(encode msg, received) = 1,
    {cases d(encode msg, received) with n h,
      {left, refl},
      {right,
      have n_succ_ne_zero : ¬n.succ = 0, from nat.succ_ne_zero n,
      have n_succ_gt_zero : n.succ > 0, from nat.pos_of_ne_zero n_succ_ne_zero,
      linarith}
    },
  cases h₁,
    {have h₂ : encode msg = received,
    from hamming.distance_zero_eq (encode msg) received h₁,
    rw h₂.symm,
    rw no_encoding_errors,
    rw decode_inverse_of_encode},
    {have h₂ : error_correct received = encode msg,
    from single_error_correctable msg received h₁,
    rw h₂,
    rw decode_inverse_of_encode}
end

def hamming74code : BECC 7 4 3 :=
{
  encode := hamming_code.encode,
  decode := hamming_code.decode,
  error_count := hamming.distance,
  error_correct := hamming_code.error_correct,
  error_correcting_code := hamming_code.one_error_correcting_code,
}

end hamming_code


import Mathlib.Algebra.BigOperators.Basic
import Mathlib.Algebra.BigOperators.Order
import Mathlib.Data.Int.Basic
import Mathlib.Data.Finset.Card
import Mathlib.Tactic.LibrarySearch
import Mathlib.Tactic.Linarith
import Mathlib.Tactic.Positivity

/-!
# USA Mathematical Olympiad 1998, Problem 5

Prove that for each n ≥ 2, there is a set S of n integers such that
(a-b)² divides ab for every distinct a,b ∈ S.
-/

namespace Usa1998Q5

open BigOperators

lemma usa1998_q5_stronger (n : ℕ) :
    ∃ S : Finset ℤ,
       (∀ x ∈ S, 0 ≤ x) ∧
       S.card = n ∧
       ∀ a ∈ S, ∀ b ∈ S, a ≠ b → (a - b)^2 ∣ a * b := by
  -- (Adapation of informal proof from Andreescu & Feng.)
  induction n with
  | zero => use ∅; simp
  | succ n ih =>
    obtain ⟨Sp, sp_nonnegative, sp_card, hsp⟩ := ih
    let L : ℤ := ∏ s in Sp, ∏ t in Sp.erase s, (s-t)^2

    have all_terms_pos :
      ∀ s ∈ Sp, ∀ t ∈ Sp.erase s, 0 < (s-t)^2 := by
          intros s _hs t ht
          obtain ⟨t_ne_s, _⟩ := Finset.mem_erase.mp ht
          have : s - t ≠ 0 := sub_ne_zero_of_ne t_ne_s.symm
          positivity

    have inner_prod_pos :
      ∀ s ∈ Sp, 0 < ∏ t in Sp.erase s, (s-t)^2 := by
        intros s hs
        exact Finset.prod_pos (all_terms_pos s hs)

    have L_pos : 0 < L := Finset.prod_pos inner_prod_pos

    -- Define Sₙ₊₁ = { L + a : a ∈ Sₙ } ∪ { 0 }.
    let S : Finset ℤ := Sp.map (addLeftEmbedding L) ∪ {0}
    use S

    constructor
    · -- all elements are nonnegative
      intros x hx
      rw[Finset.mem_union] at hx
      cases hx with
      | inl hx =>
        rw[Finset.mem_map] at hx
        obtain ⟨w, hw1, hw2⟩ := hx
        have := sp_nonnegative w hw1
        replace hw2 : L + w = x := hw2
        linarith
      | inr hx => simp_all
    · constructor
      · --cardinality is n + 1
        have hdisj : Disjoint (Finset.map (addLeftEmbedding L) Sp) {0} := by
          intros X hX h0 y hy
          have hyy := h0 hy
          rw[Finset.mem_singleton] at hyy
          rw[hyy] at hy
          have hxx := hX hy
          rw[Finset.mem_map] at hxx
          obtain ⟨z, hz, hz2⟩ := hxx
          replace hz2 : L + z = 0 := hz2
          have := sp_nonnegative z hz
          clear hX h0 -- Finset ≤ relation confuses linarith
          linarith
        rw[Finset.card_disjoint_union hdisj, Finset.card_singleton,
           Finset.card_map, sp_card]
      · intros α hα β hβ α_ne_β
        rw[Finset.mem_union, Finset.mem_map] at hα hβ
        -- If α,β ∈ Sₙ₊₁ and either α or β is zero, then (α - β)² divides αβ.
        cases hα with
        | inr hα => simp_all
        | inl hα =>
          cases hβ with
          | inr hβ => simp_all
          | inl hβ =>
            obtain ⟨a, ha, ha2⟩ := hα
            replace ha2 : L + a = α := ha2
            obtain ⟨b, hb, hb2⟩ := hβ
            replace hb2 : L + b = β := hb2
            have a_ne_b : a ≠ b := by aesop
            have ih := hsp a ha b hb a_ne_b
            have h5 : L = (∏ t in Sp.erase a, (a-t)^2) *
                         ∏ s in Sp.erase a, ∏ t in Sp.erase s, (s-t)^2 :=
              (Finset.mul_prod_erase Sp _ ha).symm
            have hbb := Finset.mem_erase.mpr ⟨a_ne_b.symm, hb⟩
            have h6 : (a-b)^2 * ∏ t in (Sp.erase a).erase b, (a-t)^2 =
                      ∏ t in Sp.erase a, (a-t)^2 :=
               Finset.mul_prod_erase (Sp.erase a) (fun x ↦ (a-x)^2) hbb
            have Lmod : L % (a - b)^2 = 0 := by
                 rw[h5, ←h6, mul_assoc, Int.mul_emod, Int.emod_self]
                 norm_num
            have Lmod' : (L + a) * (L + b) % (a-b)^2 = 0 := by
               have h7 : (L + a) * (L + b) = L * (L + a + b) + a * b := by ring
               rw[h7]
               rw[Int.add_emod]
               have h8 : L * (L + a + b) % (a - b) ^ 2 = 0 := by
                 rw[Int.mul_emod, Lmod]
                 norm_num
               have h9 :  a * b % (a - b) ^ 2 = 0 := Int.emod_eq_zero_of_dvd ih
               rw[h8, h9]
               norm_num
            have hab : a-b = α - β := by linarith
            rw[ha2, hb2, hab] at Lmod'
            exact Int.dvd_of_emod_eq_zero Lmod'

theorem usa1998_q5 (n : ℕ) (_hn : 2 ≤ n) :
    ∃ S : Finset ℤ,
       S.card = n ∧
       ∀ a ∈ S, ∀ b ∈ S, a ≠ b → (a - b)^2 ∣ a * b := by
  obtain ⟨S, _, hS⟩ := usa1998_q5_stronger n
  exact ⟨S, hS⟩


-- Copyright © 2019 François G. Dorais. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.

import .basic
import .semilattice

namespace algebra

signature lattice (α : Type*) :=
(join : α → α → α)
(meet : α → α → α)

namespace lattice_sig
variables {α : Type*} (s : lattice_sig α)

@[signature_instance]
definition to_join_semilattice : semilattice_sig α :=
{ op := s.join
}

@[signature_instance]
definition to_meet_semilattice : semilattice_sig α :=
{ op := s.meet
}

end lattice_sig

variables {α : Type*} (s : lattice_sig α)
local infix ∪ := s.join
local infix ∩ := s.meet

@[theory]
class lattice : Prop := intro ::
(join_associative : identity.op_associative s.join)
(join_commutative : identity.op_commutative s.join)
(join_right_absorptive : identity.op_right_absorptive s.join s.meet)
(meet_associative : identity.op_associative s.meet)
(meet_commutative : identity.op_commutative s.meet)
(meet_right_absorptive : identity.op_right_absorptive s.meet s.join)

namespace lattice
variable [i : lattice s]
include i

@[identity_instance]
theorem join_left_absorptive : identity.op_left_absorptive s.join s.meet :=
λ x y, calc (x ∩ y) ∪ y
= y ∪ (x ∩ y) : by rw op_commutative s.join ...
= y ∪ (y ∩ x) : by rw op_commutative s.meet ...
= y : by rw op_right_absorptive s.join

@[identity_instance]
theorem meet_left_absorptive : identity.op_left_absorptive s.meet s.join :=
λ x y, calc (x ∪ y) ∩ y
= y ∩ (x ∪ y) : by rw op_commutative s.meet ...
= y ∩ (y ∪ x) : by rw op_commutative s.join ...
= y : by rw op_right_absorptive s.meet

@[identity_instance]
theorem join_idempotent : identity.op_idempotent s.join :=
λ x, calc x ∪ x
= x ∪ (x ∩ (x ∪ x)) : by rw op_right_absorptive s.meet ...
= x : by rw op_right_absorptive s.join

@[identity_instance]
theorem meet_idempotent : identity.op_idempotent s.meet :=
λ x, calc x ∩ x
= x ∩ (x ∪ (x ∩ x)) : by rw op_right_absorptive s.join ...
= x : by rw op_right_absorptive s.meet

instance to_join_semilattice : semilattice s.to_join_semilattice := semilattice.infer _

instance to_meet_semilattice : semilattice s.to_meet_semilattice := semilattice.infer _

end lattice

@[theory]
class distributive_lattice : Prop := intro ::
(join_associative : identity.op_associative s.join)
(join_commutative : identity.op_commutative s.join)
(join_right_absorptive : identity.op_right_absorptive s.join s.meet)
(meet_associative : identity.op_associative s.meet)
(meet_commutative : identity.op_commutative s.meet)
(meet_right_absorptive : identity.op_right_absorptive s.meet s.join)
(meet_join_right_distributive : identity.op_right_distributive s.meet s.join)

namespace distributive_lattice
variable [i : distributive_lattice s]
include i

instance to_lattice : lattice s := lattice.infer _

@[identity_instance]
theorem meet_join_left_distributive : identity.op_left_distributive s.meet s.join :=
λ x y z, calc (x ∪ y) ∩ z
= z ∩ (x ∪ y) : by rw op_commutative s.meet ...
= (z ∩ x) ∪ (z ∩ y) : by rw op_right_distributive s.meet s.join ...
= (x ∩ z) ∪ (z ∩ y) : by rw op_commutative s.meet x z ...
= (x ∩ z) ∪ (y ∩ z) : by rw op_commutative s.meet y z

@[identity_instance]
theorem join_meet_left_distributive : identity.op_left_distributive s.join s.meet :=
λ x y z, calc (x ∩ y) ∪ z
= (x ∩ y) ∪ ((y ∩ z) ∪ z) : by rw op_left_absorptive s.join s.meet ...
= (x ∩ y) ∪ ((z ∩ y) ∪ z) : by rw op_commutative s.meet y z ...
= ((x ∩ y) ∪ (z ∩ y)) ∪ z : by rw op_associative s.join ...
= ((x ∪ z) ∩ y) ∪ z : by rw op_left_distributive s.meet s.join ...
= ((x ∪ z) ∩ y) ∪ ((x ∪ z) ∩ z) : by rw op_left_absorptive s.meet s.join ...
= (x ∪ z) ∩ (y ∪ z) : by rw op_right_distributive s.meet s.join

@[identity_instance]
theorem join_meet_right_distributive : identity.op_right_distributive s.join s.meet :=
λ x y z, calc x ∪ (y ∩ z)
= (y ∩ z) ∪ x : by rw op_commutative s.join ...
= (y ∪ x) ∩ (z ∪ x) : by rw op_left_distributive s.join s.meet ...
= (x ∪ y) ∩ (z ∪ x) : by rw op_commutative s.join x y ...
= (x ∪ y) ∩ (x ∪ z) : by rw op_commutative s.join x z

@[identity_instance]
theorem join_meet_cancel_left : identity.op_op_left_cancellative s.join s.meet :=
λ x y z hjoin hmeet,
calc y
= (x ∪ y) ∩ y : by rw op_left_absorptive s.meet ...
= (x ∪ z) ∩ y : by rw hjoin ...
= (x ∩ y) ∪ (z ∩ y) : by rw op_left_distributive s.meet s.join ...
= (x ∩ z) ∪ (z ∩ y) : by rw hmeet ...
= (x ∩ z) ∪ (y ∩ z) : by rw op_commutative s.meet y z ...
= (x ∪ y) ∩ z : by rw op_left_distributive s.meet s.join ...
= (x ∪ z) ∩ z : by rw hjoin ...
= z : by rw op_left_absorptive s.meet s.join

@[identity_instance]
theorem join_meet_cancel_right : identity.op_op_right_cancellative s.join s.meet :=
λ x z y hjoin hmeet,
have hjoin' : z ∪ x = z ∪ y,
from calc z ∪ x
= x ∪ z : by rw op_commutative s.join ...
= y ∪ z : by rw hjoin ...
= z ∪ y : by rw op_commutative s.join,
have hmeet' : z ∩ x = z ∩ y,
from calc z ∩ x
= x ∩ z : by rw op_commutative s.meet ...
= y ∩ z : by rw hmeet ...
= z ∩ y : by rw op_commutative s.meet,
join_meet_cancel_left s hjoin' hmeet'

end distributive_lattice

end algebra


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

! This file was ported from Lean 3 source module data.nat.totient
! leanprover-community/mathlib commit 5cc2dfdd3e92f340411acea4427d701dc7ed26f8
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.CharP.Two
import Mathbin.Data.Nat.Factorization.Basic
import Mathbin.Data.Nat.Periodic
import Mathbin.Data.Zmod.Basic

/-!
# Euler's totient function

This file defines [Euler's totient function](https://en.wikipedia.org/wiki/Euler's_totient_function)
`nat.totient n` which counts the number of naturals less than `n` that are coprime with `n`.
We prove the divisor sum formula, namely that `n` equals `φ` summed over the divisors of `n`. See
`sum_totient`. We also prove two lemmas to help compute totients, namely `totient_mul` and
`totient_prime_pow`.
-/

open Finset

open BigOperators

namespace Nat

#print Nat.totient /-
/-- Euler's totient function. This counts the number of naturals strictly less than `n` which are
coprime with `n`. -/
def totient (n : ℕ) : ℕ :=
  ((range n).filterₓ n.coprime).card
#align nat.totient Nat.totient
-/

-- mathport name: nat.totient
scoped notation "φ" => Nat.totient

#print Nat.totient_zero /-
@[simp]
theorem totient_zero : φ 0 = 0 :=
  rfl
#align nat.totient_zero Nat.totient_zero
-/

#print Nat.totient_one /-
@[simp]
theorem totient_one : φ 1 = 1 := by simp [totient]
#align nat.totient_one Nat.totient_one
-/

/- warning: nat.totient_eq_card_coprime -> Nat.totient_eq_card_coprime is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Eq.{1} Nat (Nat.totient n) (Finset.card.{0} Nat (Finset.filter.{0} Nat (Nat.coprime n) (fun (a : Nat) => Nat.coprime.decidable n a) (Finset.range n)))
but is expected to have type
  forall (n : Nat), Eq.{1} Nat (Nat.totient n) (Finset.card.{0} Nat (Finset.filter.{0} Nat (Nat.coprime n) (fun (a : Nat) => Nat.instDecidableCoprime_1 n a) (Finset.range n)))
Case conversion may be inaccurate. Consider using '#align nat.totient_eq_card_coprime Nat.totient_eq_card_coprimeₓ'. -/
theorem totient_eq_card_coprime (n : ℕ) : φ n = ((range n).filterₓ n.coprime).card :=
  rfl
#align nat.totient_eq_card_coprime Nat.totient_eq_card_coprime

#print Nat.totient_eq_card_lt_and_coprime /-
/-- A characterisation of `nat.totient` that avoids `finset`. -/
theorem totient_eq_card_lt_and_coprime (n : ℕ) : φ n = Nat.card { m | m < n ∧ n.coprime m } :=
  by
  let e : { m | m < n ∧ n.coprime m } ≃ Finset.filter n.coprime (Finset.range n) :=
    { toFun := fun m => ⟨m, by simpa only [Finset.mem_filter, Finset.mem_range] using m.property⟩
      invFun := fun m => ⟨m, by simpa only [Finset.mem_filter, Finset.mem_range] using m.property⟩
      left_inv := fun m => by simp only [Subtype.coe_mk, Subtype.coe_eta]
      right_inv := fun m => by simp only [Subtype.coe_mk, Subtype.coe_eta] }
  rw [totient_eq_card_coprime, card_congr e, card_eq_fintype_card, Fintype.card_coe]
#align nat.totient_eq_card_lt_and_coprime Nat.totient_eq_card_lt_and_coprime
-/

#print Nat.totient_le /-
theorem totient_le (n : ℕ) : φ n ≤ n :=
  ((range n).card_filter_le _).trans_eq (card_range n)
#align nat.totient_le Nat.totient_le
-/

#print Nat.totient_lt /-
theorem totient_lt (n : ℕ) (hn : 1 < n) : φ n < n :=
  (card_lt_card (filter_ssubset.2 ⟨0, by simp [hn.ne', pos_of_gt hn]⟩)).trans_eq (card_range n)
#align nat.totient_lt Nat.totient_lt
-/

#print Nat.totient_pos /-
theorem totient_pos : ∀ {n : ℕ}, 0 < n → 0 < φ n
  | 0 => by decide
  | 1 => by simp [totient]
  | n + 2 => fun h => card_pos.2 ⟨1, mem_filter.2 ⟨mem_range.2 (by decide), coprime_one_right _⟩⟩
#align nat.totient_pos Nat.totient_pos
-/

/- warning: nat.filter_coprime_Ico_eq_totient -> Nat.filter_coprime_Ico_eq_totient is a dubious translation:
lean 3 declaration is
  forall (a : Nat) (n : Nat), Eq.{1} Nat (Finset.card.{0} Nat (Finset.filter.{0} Nat (Nat.coprime a) (fun (a_1 : Nat) => Nat.coprime.decidable a a_1) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder n (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n a)))) (Nat.totient a)
but is expected to have type
  forall (a : Nat) (n : Nat), Eq.{1} Nat (Finset.card.{0} Nat (Finset.filter.{0} Nat (Nat.coprime a) (fun (a_1 : Nat) => Nat.instDecidableCoprime_1 a a_1) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring n (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n a)))) (Nat.totient a)
Case conversion may be inaccurate. Consider using '#align nat.filter_coprime_Ico_eq_totient Nat.filter_coprime_Ico_eq_totientₓ'. -/
theorem filter_coprime_Ico_eq_totient (a n : ℕ) :
    ((Ico n (n + a)).filterₓ (coprime a)).card = totient a :=
  by
  rw [totient, filter_Ico_card_eq_of_periodic, count_eq_card_filter_range]
  exact periodic_coprime a
#align nat.filter_coprime_Ico_eq_totient Nat.filter_coprime_Ico_eq_totient

/- warning: nat.Ico_filter_coprime_le -> Nat.Ico_filter_coprime_le is a dubious translation:
lean 3 declaration is
  forall {a : Nat} (k : Nat) (n : Nat), (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) a) -> (LE.le.{0} Nat Nat.hasLe (Finset.card.{0} Nat (Finset.filter.{0} Nat (Nat.coprime a) (fun (a_1 : Nat) => Nat.coprime.decidable a a_1) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder k (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) k n)))) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) (Nat.totient a) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) n a) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))))
but is expected to have type
  forall {a : Nat} (k : Nat) (n : Nat), (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) a) -> (LE.le.{0} Nat instLENat (Finset.card.{0} Nat (Finset.filter.{0} Nat (Nat.coprime a) (fun (a_1 : Nat) => Nat.instDecidableCoprime_1 a a_1) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring k (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) k n)))) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) (Nat.totient a) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) n a) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))
Case conversion may be inaccurate. Consider using '#align nat.Ico_filter_coprime_le Nat.Ico_filter_coprime_leₓ'. -/
theorem Ico_filter_coprime_le {a : ℕ} (k n : ℕ) (a_pos : 0 < a) :
    ((Ico k (k + n)).filterₓ (coprime a)).card ≤ totient a * (n / a + 1) :=
  by
  conv_lhs => rw [← Nat.mod_add_div n a]
  induction' n / a with i ih
  · rw [← filter_coprime_Ico_eq_totient a k]
    simp only [add_zero, mul_one, MulZeroClass.mul_zero, le_of_lt (mod_lt n a_pos)]
    mono
    refine' monotone_filter_left a.coprime _
    simp only [Finset.le_eq_subset]
    exact Ico_subset_Ico rfl.le (add_le_add_left (le_of_lt (mod_lt n a_pos)) k)
  simp only [mul_succ]
  simp_rw [← add_assoc] at ih⊢
  calc
    (Filter a.coprime (Ico k (k + n % a + a * i + a))).card =
        (Filter a.coprime
            (Ico k (k + n % a + a * i) ∪ Ico (k + n % a + a * i) (k + n % a + a * i + a))).card :=
      by
      congr
      rw [Ico_union_Ico_eq_Ico]
      rw [add_assoc]
      exact le_self_add
      exact le_self_add
    _ ≤ (Filter a.coprime (Ico k (k + n % a + a * i))).card + a.totient :=
      by
      rw [filter_union, ← filter_coprime_Ico_eq_totient a (k + n % a + a * i)]
      apply card_union_le
    _ ≤ a.totient * i + a.totient + a.totient := add_le_add_right ih (totient a)

#align nat.Ico_filter_coprime_le Nat.Ico_filter_coprime_le

open ZMod

/- warning: zmod.card_units_eq_totient -> ZMod.card_units_eq_totient is a dubious translation:
lean 3 declaration is
  forall (n : Nat) [_inst_1 : NeZero.{0} Nat Nat.hasZero n] [_inst_2 : Fintype.{0} (Units.{0} (ZMod n) (Ring.toMonoid.{0} (ZMod n) (CommRing.toRing.{0} (ZMod n) (ZMod.commRing n))))], Eq.{1} Nat (Fintype.card.{0} (Units.{0} (ZMod n) (Ring.toMonoid.{0} (ZMod n) (CommRing.toRing.{0} (ZMod n) (ZMod.commRing n)))) _inst_2) (Nat.totient n)
but is expected to have type
  forall (n : Nat) [_inst_1 : NeZero.{0} Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) n] [_inst_2 : Fintype.{0} (Units.{0} (ZMod n) (MonoidWithZero.toMonoid.{0} (ZMod n) (Semiring.toMonoidWithZero.{0} (ZMod n) (Ring.toSemiring.{0} (ZMod n) (CommRing.toRing.{0} (ZMod n) (ZMod.commRing n))))))], Eq.{1} Nat (Fintype.card.{0} (Units.{0} (ZMod n) (MonoidWithZero.toMonoid.{0} (ZMod n) (Semiring.toMonoidWithZero.{0} (ZMod n) (Ring.toSemiring.{0} (ZMod n) (CommRing.toRing.{0} (ZMod n) (ZMod.commRing n)))))) _inst_2) (Nat.totient n)
Case conversion may be inaccurate. Consider using '#align zmod.card_units_eq_totient ZMod.card_units_eq_totientₓ'. -/
/-- Note this takes an explicit `fintype ((zmod n)ˣ)` argument to avoid trouble with instance
diamonds. -/
@[simp]
theorem ZMod.card_units_eq_totient (n : ℕ) [NeZero n] [Fintype (ZMod n)ˣ] :
    Fintype.card (ZMod n)ˣ = φ n :=
  calc
    Fintype.card (ZMod n)ˣ = Fintype.card { x : ZMod n // x.val.coprime n } :=
      Fintype.card_congr ZMod.unitsEquivCoprime
    _ = φ n := by
      obtain ⟨m, rfl⟩ : ∃ m, n = m + 1 := exists_eq_succ_of_ne_zero NeZero.out
      simp only [totient, Finset.card_eq_sum_ones, Fintype.card_subtype, Finset.sum_filter, ←
        Fin.sum_univ_eq_sum_range, @Nat.coprime_comm (m + 1)]
      rfl

#align zmod.card_units_eq_totient ZMod.card_units_eq_totient

#print Nat.totient_even /-
theorem totient_even {n : ℕ} (hn : 2 < n) : Even n.totient :=
  by
  haveI : Fact (1 < n) := ⟨one_lt_two.trans hn⟩
  haveI : NeZero n := NeZero.of_gt hn
  suffices 2 = orderOf (-1 : (ZMod n)ˣ)
    by
    rw [← ZMod.card_units_eq_totient, even_iff_two_dvd, this]
    exact orderOf_dvd_card_univ
  rw [← orderOf_units, Units.coe_neg_one, orderOf_neg_one, ringChar.eq (ZMod n) n, if_neg hn.ne']
#align nat.totient_even Nat.totient_even
-/

#print Nat.totient_mul /-
theorem totient_mul {m n : ℕ} (h : m.coprime n) : φ (m * n) = φ m * φ n :=
  if hmn0 : m * n = 0 then by
    cases' Nat.mul_eq_zero.1 hmn0 with h h <;>
      simp only [totient_zero, MulZeroClass.mul_zero, MulZeroClass.zero_mul, h]
  else by
    haveI : NeZero (m * n) := ⟨hmn0⟩
    haveI : NeZero m := ⟨left_ne_zero_of_mul hmn0⟩
    haveI : NeZero n := ⟨right_ne_zero_of_mul hmn0⟩
    simp only [← ZMod.card_units_eq_totient]
    rw [Fintype.card_congr (Units.mapEquiv (ZMod.chineseRemainder h).toMulEquiv).toEquiv,
      Fintype.card_congr (@MulEquiv.prodUnits (ZMod m) (ZMod n) _ _).toEquiv, Fintype.card_prod]
#align nat.totient_mul Nat.totient_mul
-/

/- warning: nat.totient_div_of_dvd -> Nat.totient_div_of_dvd is a dubious translation:
lean 3 declaration is
  forall {n : Nat} {d : Nat}, (Dvd.Dvd.{0} Nat Nat.hasDvd d n) -> (Eq.{1} Nat (Nat.totient (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) n d)) (Finset.card.{0} Nat (Finset.filter.{0} Nat (fun (k : Nat) => Eq.{1} Nat (Nat.gcd n k) d) (fun (a : Nat) => Nat.decidableEq (Nat.gcd n a) d) (Finset.range n))))
but is expected to have type
  forall {n : Nat} {d : Nat}, (Dvd.dvd.{0} Nat Nat.instDvdNat d n) -> (Eq.{1} Nat (Nat.totient (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) n d)) (Finset.card.{0} Nat (Finset.filter.{0} Nat (fun (k : Nat) => Eq.{1} Nat (Nat.gcd n k) d) (fun (a : Nat) => instDecidableEqNat (Nat.gcd n a) d) (Finset.range n))))
Case conversion may be inaccurate. Consider using '#align nat.totient_div_of_dvd Nat.totient_div_of_dvdₓ'. -/
/-- For `d ∣ n`, the totient of `n/d` equals the number of values `k < n` such that `gcd n k = d` -/
theorem totient_div_of_dvd {n d : ℕ} (hnd : d ∣ n) :
    φ (n / d) = (filter (fun k : ℕ => n.gcd k = d) (range n)).card :=
  by
  rcases d.eq_zero_or_pos with (rfl | hd0); · simp [eq_zero_of_zero_dvd hnd]
  rcases hnd with ⟨x, rfl⟩
  rw [Nat.mul_div_cancel_left x hd0]
  apply Finset.card_congr fun k _ => d * k
  · simp only [mem_filter, mem_range, and_imp, coprime]
    refine' fun a ha1 ha2 => ⟨(mul_lt_mul_left hd0).2 ha1, _⟩
    rw [gcd_mul_left, ha2, mul_one]
  · simp [hd0.ne']
  · simp only [mem_filter, mem_range, exists_prop, and_imp]
    refine' fun b hb1 hb2 => _
    have : d ∣ b := by
      rw [← hb2]
      apply gcd_dvd_right
    rcases this with ⟨q, rfl⟩
    refine' ⟨q, ⟨⟨(mul_lt_mul_left hd0).1 hb1, _⟩, rfl⟩⟩
    rwa [gcd_mul_left, mul_right_eq_self_iff hd0] at hb2
#align nat.totient_div_of_dvd Nat.totient_div_of_dvd

#print Nat.sum_totient /-
theorem sum_totient (n : ℕ) : n.divisors.Sum φ = n :=
  by
  rcases n.eq_zero_or_pos with (rfl | hn)
  · simp
  rw [← sum_div_divisors n φ]
  have : n = ∑ d : ℕ in n.divisors, (Filter (fun k : ℕ => n.gcd k = d) (range n)).card :=
    by
    nth_rw_lhs 1 [← card_range n]
    refine' card_eq_sum_card_fiberwise fun x hx => mem_divisors.2 ⟨_, hn.ne'⟩
    apply gcd_dvd_left
  nth_rw_rhs 1 [this]
  exact sum_congr rfl fun x hx => totient_div_of_dvd (dvd_of_mem_divisors hx)
#align nat.sum_totient Nat.sum_totient
-/

/- warning: nat.sum_totient' -> Nat.sum_totient' is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Eq.{1} Nat (Finset.sum.{0, 0} Nat Nat Nat.addCommMonoid (Finset.filter.{0} Nat (fun (_x : Nat) => Dvd.Dvd.{0} Nat Nat.hasDvd _x n) (fun (a : Nat) => Nat.decidableDvd a n) (Finset.range (Nat.succ n))) (fun (m : Nat) => Nat.totient m)) n
but is expected to have type
  forall (n : Nat), Eq.{1} Nat (Finset.sum.{0, 0} Nat Nat Nat.addCommMonoid (Finset.filter.{0} Nat (fun (_x : Nat) => Dvd.dvd.{0} Nat Nat.instDvdNat _x n) (fun (a : Nat) => Nat.decidable_dvd a n) (Finset.range (Nat.succ n))) (fun (m : Nat) => Nat.totient m)) n
Case conversion may be inaccurate. Consider using '#align nat.sum_totient' Nat.sum_totient'ₓ'. -/
theorem sum_totient' (n : ℕ) : (∑ m in (range n.succ).filterₓ (· ∣ n), φ m) = n :=
  by
  convert sum_totient _ using 1
  simp only [Nat.divisors, sum_filter, range_eq_Ico]
  rw [sum_eq_sum_Ico_succ_bot] <;> simp
#align nat.sum_totient' Nat.sum_totient'

#print Nat.totient_prime_pow_succ /-
/-- When `p` is prime, then the totient of `p ^ (n + 1)` is `p ^ n * (p - 1)` -/
theorem totient_prime_pow_succ {p : ℕ} (hp : p.Prime) (n : ℕ) : φ (p ^ (n + 1)) = p ^ n * (p - 1) :=
  calc
    φ (p ^ (n + 1)) = ((range (p ^ (n + 1))).filterₓ (coprime (p ^ (n + 1)))).card :=
      totient_eq_card_coprime _
    _ = (range (p ^ (n + 1)) \ (range (p ^ n)).image (· * p)).card :=
      (congr_arg card
        (by
          rw [sdiff_eq_filter]
          apply filter_congr
          simp only [mem_range, mem_filter, coprime_pow_left_iff n.succ_pos, mem_image, not_exists,
            hp.coprime_iff_not_dvd]
          intro a ha
          constructor
          · rintro hap b _ rfl
            exact hap (dvd_mul_left _ _)
          · rintro h ⟨b, rfl⟩
            rw [pow_succ] at ha
            exact h b (lt_of_mul_lt_mul_left ha (zero_le _)) (mul_comm _ _)))
    _ = _ := by
      have h1 : Function.Injective (· * p) := mul_left_injective₀ hp.NeZero
      have h2 : (range (p ^ n)).image (· * p) ⊆ range (p ^ (n + 1)) := fun a =>
        by
        simp only [mem_image, mem_range, exists_imp]
        rintro b h rfl
        rw [pow_succ']
        exact (mul_lt_mul_right hp.pos).2 h
      rw [card_sdiff h2, card_image_of_inj_on (h1.inj_on _), card_range, card_range, ←
        one_mul (p ^ n), pow_succ, ← tsub_mul, one_mul, mul_comm]

#align nat.totient_prime_pow_succ Nat.totient_prime_pow_succ
-/

#print Nat.totient_prime_pow /-
/-- When `p` is prime, then the totient of `p ^ n` is `p ^ (n - 1) * (p - 1)` -/
theorem totient_prime_pow {p : ℕ} (hp : p.Prime) {n : ℕ} (hn : 0 < n) :
    φ (p ^ n) = p ^ (n - 1) * (p - 1) := by
  rcases exists_eq_succ_of_ne_zero (pos_iff_ne_zero.1 hn) with ⟨m, rfl⟩ <;>
    exact totient_prime_pow_succ hp _
#align nat.totient_prime_pow Nat.totient_prime_pow
-/

#print Nat.totient_prime /-
theorem totient_prime {p : ℕ} (hp : p.Prime) : φ p = p - 1 := by
  rw [← pow_one p, totient_prime_pow hp] <;> simp
#align nat.totient_prime Nat.totient_prime
-/

#print Nat.totient_eq_iff_prime /-
theorem totient_eq_iff_prime {p : ℕ} (hp : 0 < p) : p.totient = p - 1 ↔ p.Prime :=
  by
  refine' ⟨fun h => _, totient_prime⟩
  replace hp : 1 < p
  · apply lt_of_le_of_ne
    · rwa [succ_le_iff]
    · rintro rfl
      rw [totient_one, tsub_self] at h
      exact one_ne_zero h
  rw [totient_eq_card_coprime, range_eq_Ico, ← Ico_insert_succ_left hp.le, Finset.filter_insert,
    if_neg (not_coprime_of_dvd_of_dvd hp (dvd_refl p) (dvd_zero p)), ← Nat.card_Ico 1 p] at h
  refine'
    p.prime_of_coprime hp fun n hn hnz => Finset.filter_card_eq h n <| finset.mem_Ico.mpr ⟨_, hn⟩
  rwa [succ_le_iff, pos_iff_ne_zero]
#align nat.totient_eq_iff_prime Nat.totient_eq_iff_prime
-/

/- warning: nat.card_units_zmod_lt_sub_one -> Nat.card_units_zMod_lt_sub_one is a dubious translation:
lean 3 declaration is
  forall {p : Nat}, (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) p) -> (forall [_inst_1 : Fintype.{0} (Units.{0} (ZMod p) (Ring.toMonoid.{0} (ZMod p) (CommRing.toRing.{0} (ZMod p) (ZMod.commRing p))))], LE.le.{0} Nat Nat.hasLe (Fintype.card.{0} (Units.{0} (ZMod p) (Ring.toMonoid.{0} (ZMod p) (CommRing.toRing.{0} (ZMod p) (ZMod.commRing p)))) _inst_1) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) p (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {p : Nat}, (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) p) -> (forall [_inst_1 : Fintype.{0} (Units.{0} (ZMod p) (MonoidWithZero.toMonoid.{0} (ZMod p) (Semiring.toMonoidWithZero.{0} (ZMod p) (Ring.toSemiring.{0} (ZMod p) (CommRing.toRing.{0} (ZMod p) (ZMod.commRing p))))))], LE.le.{0} Nat instLENat (Fintype.card.{0} (Units.{0} (ZMod p) (MonoidWithZero.toMonoid.{0} (ZMod p) (Semiring.toMonoidWithZero.{0} (ZMod p) (Ring.toSemiring.{0} (ZMod p) (CommRing.toRing.{0} (ZMod p) (ZMod.commRing p)))))) _inst_1) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) p (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))
Case conversion may be inaccurate. Consider using '#align nat.card_units_zmod_lt_sub_one Nat.card_units_zMod_lt_sub_oneₓ'. -/
theorem card_units_zMod_lt_sub_one {p : ℕ} (hp : 1 < p) [Fintype (ZMod p)ˣ] :
    Fintype.card (ZMod p)ˣ ≤ p - 1 :=
  by
  haveI : NeZero p := ⟨(pos_of_gt hp).ne'⟩
  rw [ZMod.card_units_eq_totient p]
  exact Nat.le_pred_of_lt (Nat.totient_lt p hp)
#align nat.card_units_zmod_lt_sub_one Nat.card_units_zMod_lt_sub_one

/- warning: nat.prime_iff_card_units -> Nat.prime_iff_card_units is a dubious translation:
lean 3 declaration is
  forall (p : Nat) [_inst_1 : Fintype.{0} (Units.{0} (ZMod p) (Ring.toMonoid.{0} (ZMod p) (CommRing.toRing.{0} (ZMod p) (ZMod.commRing p))))], Iff (Nat.Prime p) (Eq.{1} Nat (Fintype.card.{0} (Units.{0} (ZMod p) (Ring.toMonoid.{0} (ZMod p) (CommRing.toRing.{0} (ZMod p) (ZMod.commRing p)))) _inst_1) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) p (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall (p : Nat) [_inst_1 : Fintype.{0} (Units.{0} (ZMod p) (MonoidWithZero.toMonoid.{0} (ZMod p) (Semiring.toMonoidWithZero.{0} (ZMod p) (Ring.toSemiring.{0} (ZMod p) (CommRing.toRing.{0} (ZMod p) (ZMod.commRing p))))))], Iff (Nat.Prime p) (Eq.{1} Nat (Fintype.card.{0} (Units.{0} (ZMod p) (MonoidWithZero.toMonoid.{0} (ZMod p) (Semiring.toMonoidWithZero.{0} (ZMod p) (Ring.toSemiring.{0} (ZMod p) (CommRing.toRing.{0} (ZMod p) (ZMod.commRing p)))))) _inst_1) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) p (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))
Case conversion may be inaccurate. Consider using '#align nat.prime_iff_card_units Nat.prime_iff_card_unitsₓ'. -/
theorem prime_iff_card_units (p : ℕ) [Fintype (ZMod p)ˣ] :
    p.Prime ↔ Fintype.card (ZMod p)ˣ = p - 1 :=
  by
  cases' eq_zero_or_neZero p with hp hp
  · subst hp
    simp only [ZMod, not_prime_zero, false_iff_iff, zero_tsub]
    -- the substI created an non-defeq but subsingleton instance diamond; resolve it
    suffices Fintype.card ℤˣ ≠ 0 by convert this
    simp
  rw [ZMod.card_units_eq_totient, Nat.totient_eq_iff_prime <| NeZero.pos p]
#align nat.prime_iff_card_units Nat.prime_iff_card_units

#print Nat.totient_two /-
@[simp]
theorem totient_two : φ 2 = 1 :=
  (totient_prime prime_two).trans rfl
#align nat.totient_two Nat.totient_two
-/

#print Nat.totient_eq_one_iff /-
theorem totient_eq_one_iff : ∀ {n : ℕ}, n.totient = 1 ↔ n = 1 ∨ n = 2
  | 0 => by simp
  | 1 => by simp
  | 2 => by simp
  | n + 3 => by
    have : 3 ≤ n + 3 := le_add_self
    simp only [succ_succ_ne_one, false_or_iff]
    exact ⟨fun h => not_even_one.elim <| h ▸ totient_even this, by rintro ⟨⟩⟩
#align nat.totient_eq_one_iff Nat.totient_eq_one_iff
-/

/-! ### Euler's product formula for the totient function

We prove several different statements of this formula. -/

#print Nat.totient_eq_prod_factorization /-
/-- Euler's product formula for the totient function. -/
theorem totient_eq_prod_factorization {n : ℕ} (hn : n ≠ 0) :
    φ n = n.factorization.Prod fun p k => p ^ (k - 1) * (p - 1) :=
  by
  rw [multiplicative_factorization φ (@totient_mul) totient_one hn]
  apply Finsupp.prod_congr fun p hp => _
  have h := zero_lt_iff.mpr (finsupp.mem_support_iff.mp hp)
  rw [totient_prime_pow (prime_of_mem_factorization hp) h]
#align nat.totient_eq_prod_factorization Nat.totient_eq_prod_factorization
-/

/- warning: nat.totient_mul_prod_factors -> Nat.totient_mul_prod_factors is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Eq.{1} Nat (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) (Nat.totient n) (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n)) (fun (p : Nat) => p))) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) n (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n)) (fun (p : Nat) => HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) p (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))))
but is expected to have type
  forall (n : Nat), Eq.{1} Nat (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) (Nat.totient n) (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n)) (fun (p : Nat) => p))) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) n (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n)) (fun (p : Nat) => HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) p (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))
Case conversion may be inaccurate. Consider using '#align nat.totient_mul_prod_factors Nat.totient_mul_prod_factorsₓ'. -/
/-- Euler's product formula for the totient function. -/
theorem totient_mul_prod_factors (n : ℕ) :
    (φ n * ∏ p in n.factors.toFinset, p) = n * ∏ p in n.factors.toFinset, p - 1 :=
  by
  by_cases hn : n = 0; · simp [hn]
  rw [totient_eq_prod_factorization hn]
  nth_rw 3 [← factorization_prod_pow_eq_self hn]
  simp only [← prod_factorization_eq_prod_factors, ← Finsupp.prod_mul]
  refine' Finsupp.prod_congr fun p hp => _
  rw [Finsupp.mem_support_iff, ← zero_lt_iff] at hp
  rw [mul_comm, ← mul_assoc, ← pow_succ, Nat.sub_add_cancel hp]
#align nat.totient_mul_prod_factors Nat.totient_mul_prod_factors

/- warning: nat.totient_eq_div_factors_mul -> Nat.totient_eq_div_factors_mul is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Eq.{1} Nat (Nat.totient n) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) n (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n)) (fun (p : Nat) => p))) (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n)) (fun (p : Nat) => HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) p (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))))
but is expected to have type
  forall (n : Nat), Eq.{1} Nat (Nat.totient n) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) n (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n)) (fun (p : Nat) => p))) (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n)) (fun (p : Nat) => HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) p (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))
Case conversion may be inaccurate. Consider using '#align nat.totient_eq_div_factors_mul Nat.totient_eq_div_factors_mulₓ'. -/
/-- Euler's product formula for the totient function. -/
theorem totient_eq_div_factors_mul (n : ℕ) :
    φ n = (n / ∏ p in n.factors.toFinset, p) * ∏ p in n.factors.toFinset, p - 1 :=
  by
  rw [← mul_div_left n.totient, totient_mul_prod_factors, mul_comm,
    Nat.mul_div_assoc _ (prod_prime_factors_dvd n), mul_comm]
  simpa [prod_factorization_eq_prod_factors] using prod_pos fun p => pos_of_mem_factorization
#align nat.totient_eq_div_factors_mul Nat.totient_eq_div_factors_mul

/- warning: nat.totient_eq_mul_prod_factors -> Nat.totient_eq_mul_prod_factors is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Eq.{1} Rat ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) (Nat.totient n)) (HMul.hMul.{0, 0, 0} Rat Rat Rat (instHMul.{0} Rat Rat.hasMul) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) n) (Finset.prod.{0, 0} Rat Nat Rat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n)) (fun (p : Nat) => HSub.hSub.{0, 0, 0} Rat Rat Rat (instHSub.{0} Rat (SubNegMonoid.toHasSub.{0} Rat (AddGroup.toSubNegMonoid.{0} Rat Rat.addGroup))) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne))) (Inv.inv.{0} Rat Rat.hasInv ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) p)))))
but is expected to have type
  forall (n : Nat), Eq.{1} Rat (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) (Nat.totient n)) (HMul.hMul.{0, 0, 0} Rat Rat Rat (instHMul.{0} Rat Rat.instMulRat) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) n) (Finset.prod.{0, 0} Rat Nat Rat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n)) (fun (p : Nat) => HSub.hSub.{0, 0, 0} Rat Rat Rat (instHSub.{0} Rat Rat.instSubRat) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1)) (Inv.inv.{0} Rat Rat.instInvRat (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p)))))
Case conversion may be inaccurate. Consider using '#align nat.totient_eq_mul_prod_factors Nat.totient_eq_mul_prod_factorsₓ'. -/
/-- Euler's product formula for the totient function. -/
theorem totient_eq_mul_prod_factors (n : ℕ) : (φ n : ℚ) = n * ∏ p in n.factors.toFinset, 1 - p⁻¹ :=
  by
  by_cases hn : n = 0
  · simp [hn]
  have hn' : (n : ℚ) ≠ 0 := by simp [hn]
  have hpQ : (∏ p in n.factors.to_finset, (p : ℚ)) ≠ 0 :=
    by
    rw [← cast_prod, cast_ne_zero, ← zero_lt_iff, ← prod_factorization_eq_prod_factors]
    exact prod_pos fun p hp => pos_of_mem_factorization hp
  simp only [totient_eq_div_factors_mul n, prod_prime_factors_dvd n, cast_mul, cast_prod,
    cast_div_char_zero, mul_comm_div, mul_right_inj' hn', div_eq_iff hpQ, ← prod_mul_distrib]
  refine' prod_congr rfl fun p hp => _
  have hp := pos_of_mem_factors (list.mem_to_finset.mp hp)
  have hp' : (p : ℚ) ≠ 0 := cast_ne_zero.mpr hp.ne.symm
  rw [sub_mul, one_mul, mul_comm, mul_inv_cancel hp', cast_pred hp]
#align nat.totient_eq_mul_prod_factors Nat.totient_eq_mul_prod_factors

#print Nat.totient_gcd_mul_totient_mul /-
theorem totient_gcd_mul_totient_mul (a b : ℕ) : φ (a.gcd b) * φ (a * b) = φ a * φ b * a.gcd b :=
  by
  have shuffle :
    ∀ a1 a2 b1 b2 c1 c2 : ℕ,
      b1 ∣ a1 → b2 ∣ a2 → a1 / b1 * c1 * (a2 / b2 * c2) = a1 * a2 / (b1 * b2) * (c1 * c2) :=
    by
    intro a1 a2 b1 b2 c1 c2 h1 h2
    calc
      a1 / b1 * c1 * (a2 / b2 * c2) = a1 / b1 * (a2 / b2) * (c1 * c2) := by apply mul_mul_mul_comm
      _ = a1 * a2 / (b1 * b2) * (c1 * c2) := by
        congr 1
        exact div_mul_div_comm h1 h2

  simp only [totient_eq_div_factors_mul]
  rw [shuffle, shuffle]
  rotate_left
  repeat' apply prod_prime_factors_dvd
  · simp only [prod_factors_gcd_mul_prod_factors_mul]
    rw [eq_comm, mul_comm, ← mul_assoc, ← Nat.mul_div_assoc]
    exact mul_dvd_mul (prod_prime_factors_dvd a) (prod_prime_factors_dvd b)
#align nat.totient_gcd_mul_totient_mul Nat.totient_gcd_mul_totient_mul
-/

#print Nat.totient_super_multiplicative /-
theorem totient_super_multiplicative (a b : ℕ) : φ a * φ b ≤ φ (a * b) :=
  by
  let d := a.gcd b
  rcases(zero_le a).eq_or_lt with (rfl | ha0)
  · simp
  have hd0 : 0 < d := Nat.gcd_pos_of_pos_left _ ha0
  rw [← mul_le_mul_right hd0, ← totient_gcd_mul_totient_mul a b, mul_comm]
  apply mul_le_mul_left' (Nat.totient_le d)
#align nat.totient_super_multiplicative Nat.totient_super_multiplicative
-/

#print Nat.totient_dvd_of_dvd /-
theorem totient_dvd_of_dvd {a b : ℕ} (h : a ∣ b) : φ a ∣ φ b :=
  by
  rcases eq_or_ne a 0 with (rfl | ha0)
  · simp [zero_dvd_iff.1 h]
  rcases eq_or_ne b 0 with (rfl | hb0)
  · simp
  have hab' : a.factorization.support ⊆ b.factorization.support :=
    by
    intro p
    simp only [support_factorization, List.mem_toFinset]
    apply factors_subset_of_dvd h hb0
  rw [totient_eq_prod_factorization ha0, totient_eq_prod_factorization hb0]
  refine' Finsupp.prod_dvd_prod_of_subset_of_dvd hab' fun p hp => mul_dvd_mul _ dvd_rfl
  exact pow_dvd_pow p (tsub_le_tsub_right ((factorization_le_iff_dvd ha0 hb0).2 h p) 1)
#align nat.totient_dvd_of_dvd Nat.totient_dvd_of_dvd
-/

#print Nat.totient_mul_of_prime_of_dvd /-
theorem totient_mul_of_prime_of_dvd {p n : ℕ} (hp : p.Prime) (h : p ∣ n) :
    (p * n).totient = p * n.totient :=
  by
  have h1 := totient_gcd_mul_totient_mul p n
  rw [gcd_eq_left h, mul_assoc] at h1
  simpa [(totient_pos hp.pos).ne', mul_comm] using h1
#align nat.totient_mul_of_prime_of_dvd Nat.totient_mul_of_prime_of_dvd
-/

#print Nat.totient_mul_of_prime_of_not_dvd /-
theorem totient_mul_of_prime_of_not_dvd {p n : ℕ} (hp : p.Prime) (h : ¬p ∣ n) :
    (p * n).totient = (p - 1) * n.totient :=
  by
  rw [totient_mul _, totient_prime hp]
  simpa [h] using coprime_or_dvd_of_prime hp n
#align nat.totient_mul_of_prime_of_not_dvd Nat.totient_mul_of_prime_of_not_dvd
-/

end Nat


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

! This file was ported from Lean 3 source module topology.order.priestley
! leanprover-community/mathlib commit 50832daea47b195a48b5b33b1c8b2162c48c3afc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.UpperLower.Basic
import Mathbin.Topology.Separation

/-!
# Priestley spaces

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

This file defines Priestley spaces. A Priestley space is an ordered compact topological space such
that any two distinct points can be separated by a clopen upper set.

## Main declarations

* `priestley_space`: Prop-valued mixin stating the Priestley separation axiom: Any two distinct
  points can be separated by a clopen upper set.

## Implementation notes

We do not include compactness in the definition, so a Priestley space is to be declared as follows:
`[preorder α] [topological_space α] [compact_space α] [priestley_space α]`

## References

* [Wikipedia, *Priestley space*](https://en.wikipedia.org/wiki/Priestley_space)
* [Davey, Priestley *Introduction to Lattices and Order*][davey_priestley]
-/

open Set

variable {α : Type _}

#print PriestleySpace /-
/-- A Priestley space is an ordered topological space such that any two distinct points can be
separated by a clopen upper set. Compactness is often assumed, but we do not include it here. -/
class PriestleySpace (α : Type _) [Preorder α] [TopologicalSpace α] where
  priestley {x y : α} : ¬x ≤ y → ∃ U : Set α, IsClopen U ∧ IsUpperSet U ∧ x ∈ U ∧ y ∉ U
#align priestley_space PriestleySpace
-/

variable [TopologicalSpace α]

section Preorder

variable [Preorder α] [PriestleySpace α] {x y : α}

#print exists_clopen_upper_of_not_le /-
theorem exists_clopen_upper_of_not_le :
    ¬x ≤ y → ∃ U : Set α, IsClopen U ∧ IsUpperSet U ∧ x ∈ U ∧ y ∉ U :=
  PriestleySpace.priestley
#align exists_clopen_upper_of_not_le exists_clopen_upper_of_not_le
-/

#print exists_clopen_lower_of_not_le /-
theorem exists_clopen_lower_of_not_le (h : ¬x ≤ y) :
    ∃ U : Set α, IsClopen U ∧ IsLowerSet U ∧ x ∉ U ∧ y ∈ U :=
  let ⟨U, hU, hU', hx, hy⟩ := exists_clopen_upper_of_not_le h
  ⟨Uᶜ, hU.compl, hU'.compl, Classical.not_not.2 hx, hy⟩
#align exists_clopen_lower_of_not_le exists_clopen_lower_of_not_le
-/

end Preorder

section PartialOrder

variable [PartialOrder α] [PriestleySpace α] {x y : α}

#print exists_clopen_upper_or_lower_of_ne /-
theorem exists_clopen_upper_or_lower_of_ne (h : x ≠ y) :
    ∃ U : Set α, IsClopen U ∧ (IsUpperSet U ∨ IsLowerSet U) ∧ x ∈ U ∧ y ∉ U :=
  by
  obtain h | h := h.not_le_or_not_le
  · exact (exists_clopen_upper_of_not_le h).imp fun U => And.imp_right <| And.imp_left Or.inl
  · obtain ⟨U, hU, hU', hy, hx⟩ := exists_clopen_lower_of_not_le h
    exact ⟨U, hU, Or.inr hU', hx, hy⟩
#align exists_clopen_upper_or_lower_of_ne exists_clopen_upper_or_lower_of_ne
-/

#print PriestleySpace.toT2Space /-
-- See note [lower instance priority]
instance (priority := 100) PriestleySpace.toT2Space : T2Space α :=
  ⟨fun x y h =>
    let ⟨U, hU, _, hx, hy⟩ := exists_clopen_upper_or_lower_of_ne h
    ⟨U, Uᶜ, hU.IsOpen, hU.compl.IsOpen, hx, hy, disjoint_compl_right⟩⟩
#align priestley_space.to_t2_space PriestleySpace.toT2Space
-/

end PartialOrder


/-
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 Mathlib.Algebra.Order.EuclideanAbsoluteValue
import Mathlib.Data.Polynomial.FieldDivision

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

Let `𝔽_q` 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.cardPowDegree` 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.cardPowDegree_isEuclidean`: `cardPowDegree` respects the
   Euclidean domain structure on the ring of polynomials

-/

namespace Polynomial

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

open AbsoluteValue

open Classical Polynomial

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

`cardPowDegree 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.natDegree q.natDegree).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

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

-- @[simp] -- Porting note: simp can prove this
theorem cardPowDegree_zero : cardPowDegree (0 : Fq[X]) = 0 :=
  if_pos rfl
#align polynomial.card_pow_degree_zero Polynomial.cardPowDegree_zero

@[simp]

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
      show cardPowDegree p < cardPowDegree q ↔ degree p < degree q
      simp only [cardPowDegree_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_natDegree hp, degree_eq_natDegree hq, Nat.cast_withBot, Nat.cast_withBot,
          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) 2021 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies
-/
import data.sigma.lex
import order.bounded_order

/-!
# Orders on a sigma type

This file defines two orders on a sigma type:
* The disjoint sum of orders. `a` is less `b` iff `a` and `b` are in the same summand and `a` is
  less than `b` there.
* The lexicographical order. `a` is less than `b` if its summand is strictly less than the summand
  of `b` or they are in the same summand and `a` is less than `b` there.

We make the disjoint sum of orders the default set of instances. The lexicographic order goes on a
type synonym.

## Notation

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

## See also

Related files are:
* `data.finset.colex`: Colexicographic order on finite sets.
* `data.list.lex`: Lexicographic order on lists.
* `data.pi.lex`: Lexicographic order on `Πₗ i, α i`.
* `data.psigma.order`: Lexicographic order on `Σₗ' i, α i`. Basically a twin of this file.
* `data.prod.lex`: Lexicographic order on `α × β`.

## TODO

Prove that a sigma type is a `no_max_order`, `no_min_order`, `densely_ordered` when its summands
are.

Upgrade `equiv.sigma_congr_left`, `equiv.sigma_congr`, `equiv.sigma_assoc`,
`equiv.sigma_prod_of_equiv`, `equiv.sigma_equiv_prod`, ... to order isomorphisms.
-/

namespace sigma
variables {ι : Type*} {α : ι → Type*}

/-! ### Disjoint sum of orders on `sigma` -/

/-- Disjoint sum of orders. `⟨i, a⟩ ≤ ⟨j, b⟩` iff `i = j` and `a ≤ b`. -/
inductive le [Π i, has_le (α i)] : Π a b : Σ i, α i, Prop
| fiber (i : ι) (a b : α i) : a ≤ b → le ⟨i, a⟩ ⟨i, b⟩

/-- Disjoint sum of orders. `⟨i, a⟩ < ⟨j, b⟩` iff `i = j` and `a < b`. -/
inductive lt [Π i, has_lt (α i)] : Π a b : Σ i, α i, Prop
| fiber (i : ι) (a b : α i) : a < b → lt ⟨i, a⟩ ⟨i, b⟩

instance [Π i, has_le (α i)] : has_le (Σ i, α i) := ⟨le⟩
instance [Π i, has_lt (α i)] : has_lt (Σ i, α i) := ⟨lt⟩

@[simp] lemma mk_le_mk_iff [Π i, has_le (α i)] {i : ι} {a b : α i} :
  (⟨i, a⟩ : sigma α) ≤ ⟨i, b⟩ ↔ a ≤ b :=
⟨λ ⟨_, _, _, h⟩, h, le.fiber _ _ _⟩

@[simp] lemma mk_lt_mk_iff [Π i, has_lt (α i)] {i : ι} {a b : α i} :
  (⟨i, a⟩ : sigma α) < ⟨i, b⟩ ↔ a < b :=
⟨λ ⟨_, _, _, h⟩, h, lt.fiber _ _ _⟩

lemma lt_def [Π i, has_lt (α i)] {a b : Σ i, α i} : a < b ↔ ∃ h : a.1 = b.1, h.rec a.2 < b.2 :=
begin
  split,
  { rintro ⟨i, a, b, h⟩,
    exact ⟨rfl, h⟩ },
  { obtain ⟨i, a⟩ := a,
    obtain ⟨j, b⟩ := b,
    rintro ⟨(rfl : i = j), h⟩,
    exact lt.fiber _ _ _ h }
end

instance [Π i, preorder (α i)] : preorder (Σ i, α i) :=
{ le_refl := λ ⟨i, a⟩, le.fiber i a a le_rfl,
  le_trans := begin
    rintro _ _ _ ⟨i, a, b, hab⟩ ⟨_, _, c, hbc⟩,
    exact le.fiber i a c (hab.trans hbc),
  end,
  lt_iff_le_not_le := λ _ _, begin
    split,
    { rintro ⟨i, a, b, hab⟩,
      rwa [mk_le_mk_iff, mk_le_mk_iff, ←lt_iff_le_not_le] },
    { rintro ⟨⟨i, a, b, hab⟩, h⟩,
      rw mk_le_mk_iff at h,
      exact mk_lt_mk_iff.2 (hab.lt_of_not_le h) }
  end,
  .. sigma.has_le,
  .. sigma.has_lt }

instance [Π i, partial_order (α i)] : partial_order (Σ i, α i) :=
{ le_antisymm := begin
    rintro _ _ ⟨i, a, b, hab⟩ ⟨_, _, _, hba⟩,
    exact ext rfl (heq_of_eq $ hab.antisymm hba),
  end,
  .. sigma.preorder }

/-! ### Lexicographical order on `sigma` -/

namespace lex

notation `Σₗ` binders `, ` r:(scoped p, _root_.lex (sigma p)) := r

/-- The lexicographical `≤` on a sigma type. -/
instance has_le [has_lt ι] [Π i, has_le (α i)] : has_le (Σₗ i, α i) := ⟨lex (<) (λ i, (≤))⟩

/-- The lexicographical `<` on a sigma type. -/
instance has_lt [has_lt ι] [Π i, has_lt (α i)] : has_lt (Σₗ i, α i) := ⟨lex (<) (λ i, (<))⟩

/-- The lexicographical preorder on a sigma type. -/
instance preorder [preorder ι] [Π i, preorder (α i)] : preorder (Σₗ i, α i) :=
{ le_refl := λ ⟨i, a⟩, lex.right a a le_rfl,
  le_trans := λ _ _ _, trans_of (lex (<) $ λ _, (≤)),
  lt_iff_le_not_le := begin
    refine λ a b, ⟨λ hab, ⟨hab.mono_right (λ i a b, le_of_lt), _⟩, _⟩,
    { rintro (⟨j, i, b, a, hji⟩ | ⟨i, b, a, hba⟩);
        obtain (⟨_, _, _, _, hij⟩ | ⟨_, _, _, hab⟩) := hab,
      { exact hij.not_lt hji },
      { exact lt_irrefl _ hji },
      { exact lt_irrefl _ hij },
      { exact hab.not_le hba } },
    { rintro ⟨⟨i, j, a, b, hij⟩ |⟨i, a, b, hab⟩, hba⟩,
      { exact lex.left _ _ hij },
      { exact lex.right _ _ (hab.lt_of_not_le $ λ h, hba $ lex.right _ _ h) } }
  end,
  .. lex.has_le,
  .. lex.has_lt }

/-- The lexicographical partial order on a sigma type. -/
instance partial_order [preorder ι] [Π i, partial_order (α i)] :
  partial_order (Σₗ i, α i) :=
{ le_antisymm := λ _ _, antisymm_of (lex (<) $ λ _, (≤)),
  .. lex.preorder }

/-- The lexicographical linear order on a sigma type. -/
instance linear_order [linear_order ι] [Π i, linear_order (α i)] :
  linear_order (Σₗ i, α i) :=
{ le_total := total_of (lex (<) $ λ _, (≤)),
  decidable_eq := sigma.decidable_eq,
  decidable_le := lex.decidable _ _,
  .. lex.partial_order }

/-- The lexicographical linear order on a sigma type. -/
instance order_bot [partial_order ι] [order_bot ι] [Π i, preorder (α i)] [order_bot (α ⊥)] :
  order_bot (Σₗ i, α i) :=
{ bot := ⟨⊥, ⊥⟩,
  bot_le := λ ⟨a, b⟩, begin
    obtain rfl | ha := eq_bot_or_bot_lt a,
    { exact lex.right _ _ bot_le },
    { exact lex.left _ _ ha }
  end }

/-- The lexicographical linear order on a sigma type. -/
instance order_top [partial_order ι] [order_top ι] [Π i, preorder (α i)] [order_top (α ⊤)] :
  order_top (Σₗ i, α i) :=
{ top := ⟨⊤, ⊤⟩,
  le_top := λ ⟨a, b⟩, begin
    obtain rfl | ha := eq_top_or_lt_top a,
    { exact lex.right _ _ le_top },
    { exact lex.left _ _ ha }
  end }

/-- The lexicographical linear order on a sigma type. -/
instance bounded_order [partial_order ι] [bounded_order ι] [Π i, preorder (α i)]
  [order_bot (α ⊥)] [order_top (α ⊤)] :
  bounded_order (Σₗ i, α i) :=
{ .. lex.order_bot, .. lex.order_top }

end lex
end sigma


/-
Copyright (c) 2022 Antoine Labelle. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Antoine Labelle

! This file was ported from Lean 3 source module category_theory.closed.functor_category
! leanprover-community/mathlib commit 0caf3701139ef2e69c215717665361cda205a90b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Closed.Monoidal
import Mathbin.CategoryTheory.Monoidal.FunctorCategory

/-!
# Functors from a groupoid into a monoidal closed category form a monoidal closed category.

(Using the pointwise monoidal structure on the functor category.)
-/

noncomputable section

open CategoryTheory

open CategoryTheory.MonoidalCategory

open CategoryTheory.MonoidalClosed

namespace CategoryTheory.Functor

variable {C D : Type _} [Groupoid D] [Category C] [MonoidalCategory C] [MonoidalClosed C]

/-- Auxiliary definition for `category_theory.monoidal_closed.functor_closed`.
The internal hom functor `F ⟶[C] -` -/
@[simps]
def closedIhom (F : D ⥤ C) : (D ⥤ C) ⥤ D ⥤ C :=
  ((whiskeringRight₂ D Cᵒᵖ C C).obj internalHom).obj (Groupoid.invFunctor D ⋙ F.op)
#align category_theory.functor.closed_ihom CategoryTheory.Functor.closedIhom

/-- Auxiliary definition for `category_theory.monoidal_closed.functor_closed`.
The unit for the adjunction `(tensor_left F) ⊣ (ihom F)`. -/
@[simps]
def closedUnit (F : D ⥤ C) : 𝟭 (D ⥤ C) ⟶ tensorLeft F ⋙ closedIhom F
    where app G :=
    { app := fun X => (ihom.coev (F.obj X)).app (G.obj X)
      naturality' := by
        intro X Y f
        dsimp
        simp only [ihom.coev_naturality, closed_ihom_obj_map, monoidal.tensor_obj_map]
        dsimp
        rw [coev_app_comp_pre_app_assoc, ← functor.map_comp]
        simp }
#align category_theory.functor.closed_unit CategoryTheory.Functor.closedUnit

/-- Auxiliary definition for `category_theory.monoidal_closed.functor_closed`.
The counit for the adjunction `(tensor_left F) ⊣ (ihom F)`. -/
@[simps]
def closedCounit (F : D ⥤ C) : closedIhom F ⋙ tensorLeft F ⟶ 𝟭 (D ⥤ C)
    where app G :=
    { app := fun X => (ihom.ev (F.obj X)).app (G.obj X)
      naturality' := by
        intro X Y f
        dsimp
        simp only [closed_ihom_obj_map, pre_comm_ihom_map]
        rw [← tensor_id_comp_id_tensor, id_tensor_comp]
        simp }
#align category_theory.functor.closed_counit CategoryTheory.Functor.closedCounit

/-- If `C` is a monoidal closed category and `D` is groupoid, then every functor `F : D ⥤ C` is
closed in the functor category `F : D ⥤ C` with the pointwise monoidal structure. -/
@[simps]
instance closed (F : D ⥤ C) : Closed F
    where isAdj :=
    { right := closedIhom F
      adj :=
        Adjunction.mkOfUnitCounit
          { Unit := closedUnit F
            counit := closedCounit F } }
#align category_theory.functor.closed CategoryTheory.Functor.closed

/-- If `C` is a monoidal closed category and `D` is groupoid, then the functor category `D ⥤ C`,
with the pointwise monoidal structure, is monoidal closed. -/
@[simps]
instance monoidalClosed : MonoidalClosed (D ⥤ C) where closed' := by infer_instance
#align category_theory.functor.monoidal_closed CategoryTheory.Functor.monoidalClosed

theorem ihom_map (F : D ⥤ C) {G H : D ⥤ C} (f : G ⟶ H) : (ihom F).map f = (closedIhom F).map f :=
  rfl
#align category_theory.functor.ihom_map CategoryTheory.Functor.ihom_map

theorem ihom_ev_app (F G : D ⥤ C) : (ihom.ev F).app G = (closedCounit F).app G :=
  rfl
#align category_theory.functor.ihom_ev_app CategoryTheory.Functor.ihom_ev_app

theorem ihom_coev_app (F G : D ⥤ C) : (ihom.coev F).app G = (closedUnit F).app G :=
  rfl
#align category_theory.functor.ihom_coev_app CategoryTheory.Functor.ihom_coev_app

end CategoryTheory.Functor


/-
Copyright (c) 2021 Arthur Paulino. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Arthur Paulino, Kyle Miller

! This file was ported from Lean 3 source module combinatorics.simple_graph.coloring
! leanprover-community/mathlib commit ee05e9ce1322178f0c12004eb93c00d2c8c00ed2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Combinatorics.SimpleGraph.Clique
import Mathbin.Data.Nat.Lattice
import Mathbin.Data.Setoid.Partition
import Mathbin.Order.Antichain

/-!
# Graph Coloring

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

This module defines colorings of simple graphs (also known as proper
colorings in the literature). A graph coloring is the attribution of
"colors" to all of its vertices such that adjacent vertices have
different colors. A coloring can be represented as a homomorphism into
a complete graph, whose vertices represent the colors.

## Main definitions

* `G.coloring α` is the type of `α`-colorings of a simple graph `G`,
  with `α` being the set of available colors. The type is defined to
  be homomorphisms from `G` into the complete graph on `α`, and
  colorings have a coercion to `V → α`.

* `G.colorable n` is the proposition that `G` is `n`-colorable, which
  is whether there exists a coloring with at most *n* colors.

* `G.chromatic_number` is the minimal `n` such that `G` is
  `n`-colorable, or `0` if it cannot be colored with finitely many
  colors.

* `C.color_class c` is the set of vertices colored by `c : α` in the
  coloring `C : G.coloring α`.

* `C.color_classes` is the set containing all color classes.

## Todo:

  * Gather material from:
    * https://github.com/leanprover-community/mathlib/blob/simple_graph_matching/src/combinatorics/simple_graph/coloring.lean
    * https://github.com/kmill/lean-graphcoloring/blob/master/src/graph.lean

  * Trees

  * Planar graphs

  * Chromatic polynomials

  * develop API for partial colorings, likely as colorings of subgraphs (`H.coe.coloring α`)
-/

universe u v

namespace SimpleGraph

variable {V : Type u} (G : SimpleGraph V)

#print SimpleGraph.Coloring /-
/-- An `α`-coloring of a simple graph `G` is a homomorphism of `G` into the complete graph on `α`.
This is also known as a proper coloring.
-/
abbrev Coloring (α : Type v) :=
  G →g (⊤ : SimpleGraph α)
#align simple_graph.coloring SimpleGraph.Coloring
-/

variable {G} {α : Type v} (C : G.Coloring α)

#print SimpleGraph.Coloring.valid /-
theorem Coloring.valid {v w : V} (h : G.Adj v w) : C v ≠ C w :=
  C.map_rel h
#align simple_graph.coloring.valid SimpleGraph.Coloring.valid
-/

#print SimpleGraph.Coloring.mk /-
/-- Construct a term of `simple_graph.coloring` using a function that
assigns vertices to colors and a proof that it is as proper coloring.

(Note: this is a definitionally the constructor for `simple_graph.hom`,
but with a syntactically better proper coloring hypothesis.)
-/
@[match_pattern]
def Coloring.mk (color : V → α) (valid : ∀ {v w : V}, G.Adj v w → color v ≠ color w) :
    G.Coloring α :=
  ⟨color, @valid⟩
#align simple_graph.coloring.mk SimpleGraph.Coloring.mk
-/

#print SimpleGraph.Coloring.colorClass /-
/-- The color class of a given color.
-/
def Coloring.colorClass (c : α) : Set V :=
  { v : V | C v = c }
#align simple_graph.coloring.color_class SimpleGraph.Coloring.colorClass
-/

#print SimpleGraph.Coloring.colorClasses /-
/-- The set containing all color classes. -/
def Coloring.colorClasses : Set (Set V) :=
  (Setoid.ker C).classes
#align simple_graph.coloring.color_classes SimpleGraph.Coloring.colorClasses
-/

#print SimpleGraph.Coloring.mem_colorClass /-
theorem Coloring.mem_colorClass (v : V) : v ∈ C.colorClass (C v) :=
  rfl
#align simple_graph.coloring.mem_color_class SimpleGraph.Coloring.mem_colorClass
-/

#print SimpleGraph.Coloring.colorClasses_isPartition /-
theorem Coloring.colorClasses_isPartition : Setoid.IsPartition C.colorClasses :=
  Setoid.isPartition_classes (Setoid.ker C)
#align simple_graph.coloring.color_classes_is_partition SimpleGraph.Coloring.colorClasses_isPartition
-/

#print SimpleGraph.Coloring.mem_colorClasses /-
theorem Coloring.mem_colorClasses {v : V} : C.colorClass (C v) ∈ C.colorClasses :=
  ⟨v, rfl⟩
#align simple_graph.coloring.mem_color_classes SimpleGraph.Coloring.mem_colorClasses
-/

#print SimpleGraph.Coloring.colorClasses_finite /-
theorem Coloring.colorClasses_finite [Finite α] : C.colorClasses.Finite :=
  Setoid.finite_classes_ker _
#align simple_graph.coloring.color_classes_finite SimpleGraph.Coloring.colorClasses_finite
-/

#print SimpleGraph.Coloring.card_colorClasses_le /-
theorem Coloring.card_colorClasses_le [Fintype α] [Fintype C.colorClasses] :
    Fintype.card C.colorClasses ≤ Fintype.card α :=
  Setoid.card_classes_ker_le C
#align simple_graph.coloring.card_color_classes_le SimpleGraph.Coloring.card_colorClasses_le
-/

#print SimpleGraph.Coloring.not_adj_of_mem_colorClass /-
theorem Coloring.not_adj_of_mem_colorClass {c : α} {v w : V} (hv : v ∈ C.colorClass c)
    (hw : w ∈ C.colorClass c) : ¬G.Adj v w := fun h => C.valid h (Eq.trans hv (Eq.symm hw))
#align simple_graph.coloring.not_adj_of_mem_color_class SimpleGraph.Coloring.not_adj_of_mem_colorClass
-/

#print SimpleGraph.Coloring.color_classes_independent /-
theorem Coloring.color_classes_independent (c : α) : IsAntichain G.Adj (C.colorClass c) :=
  fun v hv w hw h => C.not_adj_of_mem_colorClass hv hw
#align simple_graph.coloring.color_classes_independent SimpleGraph.Coloring.color_classes_independent
-/

-- TODO make this computable
noncomputable instance [Fintype V] [Fintype α] : Fintype (Coloring G α) := by
  classical
    change Fintype (RelHom G.adj (⊤ : SimpleGraph α).Adj)
    apply Fintype.ofInjective _ RelHom.coe_fn_injective
    infer_instance

variable (G)

#print SimpleGraph.Colorable /-
/-- Whether a graph can be colored by at most `n` colors. -/
def Colorable (n : ℕ) : Prop :=
  Nonempty (G.Coloring (Fin n))
#align simple_graph.colorable SimpleGraph.Colorable
-/

#print SimpleGraph.coloringOfIsEmpty /-
/-- The coloring of an empty graph. -/
def coloringOfIsEmpty [IsEmpty V] : G.Coloring α :=
  Coloring.mk isEmptyElim fun v => isEmptyElim
#align simple_graph.coloring_of_is_empty SimpleGraph.coloringOfIsEmpty
-/

#print SimpleGraph.colorable_of_isEmpty /-
theorem colorable_of_isEmpty [IsEmpty V] (n : ℕ) : G.Colorable n :=
  ⟨G.coloringOfIsEmpty⟩
#align simple_graph.colorable_of_is_empty SimpleGraph.colorable_of_isEmpty
-/

#print SimpleGraph.isEmpty_of_colorable_zero /-
theorem isEmpty_of_colorable_zero (h : G.Colorable 0) : IsEmpty V :=
  by
  constructor
  intro v
  obtain ⟨i, hi⟩ := h.some v
  exact Nat.not_lt_zero _ hi
#align simple_graph.is_empty_of_colorable_zero SimpleGraph.isEmpty_of_colorable_zero
-/

#print SimpleGraph.selfColoring /-
/-- The "tautological" coloring of a graph, using the vertices of the graph as colors. -/
def selfColoring : G.Coloring V :=
  Coloring.mk id fun v w => G.ne_of_adj
#align simple_graph.self_coloring SimpleGraph.selfColoring
-/

#print SimpleGraph.chromaticNumber /-
/-- The chromatic number of a graph is the minimal number of colors needed to color it.
If `G` isn't colorable with finitely many colors, this will be 0. -/
noncomputable def chromaticNumber : ℕ :=
  infₛ { n : ℕ | G.Colorable n }
#align simple_graph.chromatic_number SimpleGraph.chromaticNumber
-/

#print SimpleGraph.recolorOfEmbedding /-
/-- Given an embedding, there is an induced embedding of colorings. -/
def recolorOfEmbedding {α β : Type _} (f : α ↪ β) : G.Coloring α ↪ G.Coloring β
    where
  toFun C := (Embedding.completeGraph f).toHom.comp C
  inj' :=
    by
    -- this was strangely painful; seems like missing lemmas about embeddings
    intro C C' h
    dsimp only at h
    ext v
    apply (embedding.complete_graph f).inj'
    change ((embedding.complete_graph f).toHom.comp C) v = _
    rw [h]
    rfl
#align simple_graph.recolor_of_embedding SimpleGraph.recolorOfEmbedding
-/

#print SimpleGraph.recolorOfEquiv /-
/-- Given an equivalence, there is an induced equivalence between colorings. -/
def recolorOfEquiv {α β : Type _} (f : α ≃ β) : G.Coloring α ≃ G.Coloring β
    where
  toFun := G.recolorOfEmbedding f.toEmbedding
  invFun := G.recolorOfEmbedding f.symm.toEmbedding
  left_inv C := by
    ext v
    apply Equiv.symm_apply_apply
  right_inv C := by
    ext v
    apply Equiv.apply_symm_apply
#align simple_graph.recolor_of_equiv SimpleGraph.recolorOfEquiv
-/

#print SimpleGraph.recolorOfCardLE /-
/-- There is a noncomputable embedding of `α`-colorings to `β`-colorings if
`β` has at least as large a cardinality as `α`. -/
noncomputable def recolorOfCardLE {α β : Type _} [Fintype α] [Fintype β]
    (hn : Fintype.card α ≤ Fintype.card β) : G.Coloring α ↪ G.Coloring β :=
  G.recolorOfEmbedding <| (Function.Embedding.nonempty_of_card_le hn).some
#align simple_graph.recolor_of_card_le SimpleGraph.recolorOfCardLE
-/

variable {G}

#print SimpleGraph.Colorable.mono /-
theorem Colorable.mono {n m : ℕ} (h : n ≤ m) (hc : G.Colorable n) : G.Colorable m :=
  ⟨G.recolorOfCardLE (by simp [h]) hc.some⟩
#align simple_graph.colorable.mono SimpleGraph.Colorable.mono
-/

#print SimpleGraph.Coloring.to_colorable /-
theorem Coloring.to_colorable [Fintype α] (C : G.Coloring α) : G.Colorable (Fintype.card α) :=
  ⟨G.recolorOfCardLE (by simp) C⟩
#align simple_graph.coloring.to_colorable SimpleGraph.Coloring.to_colorable
-/

#print SimpleGraph.colorable_of_fintype /-
theorem colorable_of_fintype (G : SimpleGraph V) [Fintype V] : G.Colorable (Fintype.card V) :=
  G.selfColoring.to_colorable
#align simple_graph.colorable_of_fintype SimpleGraph.colorable_of_fintype
-/

#print SimpleGraph.Colorable.toColoring /-
/-- Noncomputably get a coloring from colorability. -/
noncomputable def Colorable.toColoring [Fintype α] {n : ℕ} (hc : G.Colorable n)
    (hn : n ≤ Fintype.card α) : G.Coloring α :=
  by
  rw [← Fintype.card_fin n] at hn
  exact G.recolor_of_card_le hn hc.some
#align simple_graph.colorable.to_coloring SimpleGraph.Colorable.toColoring
-/

/- warning: simple_graph.colorable.of_embedding -> SimpleGraph.Colorable.of_embedding is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} {G : SimpleGraph.{u1} V} {V' : Type.{u2}} {G' : SimpleGraph.{u2} V'}, (SimpleGraph.Embedding.{u1, u2} V V' G G') -> (forall {n : Nat}, (SimpleGraph.Colorable.{u2} V' G' n) -> (SimpleGraph.Colorable.{u1} V G n))
but is expected to have type
  forall {V : Type.{u2}} {G : SimpleGraph.{u2} V} {V' : Type.{u1}} {G' : SimpleGraph.{u1} V'}, (SimpleGraph.Embedding.{u2, u1} V V' G G') -> (forall {n : Nat}, (SimpleGraph.Colorable.{u1} V' G' n) -> (SimpleGraph.Colorable.{u2} V G n))
Case conversion may be inaccurate. Consider using '#align simple_graph.colorable.of_embedding SimpleGraph.Colorable.of_embeddingₓ'. -/
theorem Colorable.of_embedding {V' : Type _} {G' : SimpleGraph V'} (f : G ↪g G') {n : ℕ}
    (h : G'.Colorable n) : G.Colorable n :=
  ⟨(h.toColoring (by simp)).comp f⟩
#align simple_graph.colorable.of_embedding SimpleGraph.Colorable.of_embedding

#print SimpleGraph.colorable_iff_exists_bdd_nat_coloring /-
theorem colorable_iff_exists_bdd_nat_coloring (n : ℕ) :
    G.Colorable n ↔ ∃ C : G.Coloring ℕ, ∀ v, C v < n :=
  by
  constructor
  · rintro hc
    have C : G.coloring (Fin n) := hc.to_coloring (by simp)
    let f := embedding.complete_graph Fin.valEmbedding
    use f.to_hom.comp C
    intro v
    cases' C with color valid
    exact Fin.is_lt (color v)
  · rintro ⟨C, Cf⟩
    refine' ⟨coloring.mk _ _⟩
    · exact fun v => ⟨C v, Cf v⟩
    · rintro v w hvw
      simp only [Fin.mk_eq_mk, Ne.def]
      exact C.valid hvw
#align simple_graph.colorable_iff_exists_bdd_nat_coloring SimpleGraph.colorable_iff_exists_bdd_nat_coloring
-/

#print SimpleGraph.colorable_set_nonempty_of_colorable /-
theorem colorable_set_nonempty_of_colorable {n : ℕ} (hc : G.Colorable n) :
    { n : ℕ | G.Colorable n }.Nonempty :=
  ⟨n, hc⟩
#align simple_graph.colorable_set_nonempty_of_colorable SimpleGraph.colorable_set_nonempty_of_colorable
-/

#print SimpleGraph.chromaticNumber_bddBelow /-
theorem chromaticNumber_bddBelow : BddBelow { n : ℕ | G.Colorable n } :=
  ⟨0, fun _ _ => zero_le _⟩
#align simple_graph.chromatic_number_bdd_below SimpleGraph.chromaticNumber_bddBelow
-/

#print SimpleGraph.chromaticNumber_le_of_colorable /-
theorem chromaticNumber_le_of_colorable {n : ℕ} (hc : G.Colorable n) : G.chromaticNumber ≤ n :=
  by
  rw [chromatic_number]
  apply cinfₛ_le chromatic_number_bdd_below
  fconstructor
  exact Classical.choice hc
#align simple_graph.chromatic_number_le_of_colorable SimpleGraph.chromaticNumber_le_of_colorable
-/

#print SimpleGraph.chromaticNumber_le_card /-
theorem chromaticNumber_le_card [Fintype α] (C : G.Coloring α) :
    G.chromaticNumber ≤ Fintype.card α :=
  cinfₛ_le chromaticNumber_bddBelow C.to_colorable
#align simple_graph.chromatic_number_le_card SimpleGraph.chromaticNumber_le_card
-/

#print SimpleGraph.colorable_chromaticNumber /-
theorem colorable_chromaticNumber {m : ℕ} (hc : G.Colorable m) : G.Colorable G.chromaticNumber :=
  by
  dsimp only [chromatic_number]
  rw [Nat.infₛ_def]
  apply Nat.find_spec
  exact colorable_set_nonempty_of_colorable hc
#align simple_graph.colorable_chromatic_number SimpleGraph.colorable_chromaticNumber
-/

#print SimpleGraph.colorable_chromaticNumber_of_fintype /-
theorem colorable_chromaticNumber_of_fintype (G : SimpleGraph V) [Finite V] :
    G.Colorable G.chromaticNumber := by
  cases nonempty_fintype V
  exact colorable_chromatic_number G.colorable_of_fintype
#align simple_graph.colorable_chromatic_number_of_fintype SimpleGraph.colorable_chromaticNumber_of_fintype
-/

#print SimpleGraph.chromaticNumber_le_one_of_subsingleton /-
theorem chromaticNumber_le_one_of_subsingleton (G : SimpleGraph V) [Subsingleton V] :
    G.chromaticNumber ≤ 1 := by
  rw [chromatic_number]
  apply cinfₛ_le chromatic_number_bdd_below
  fconstructor
  refine' coloring.mk (fun _ => 0) _
  intro v w
  rw [Subsingleton.elim v w]
  simp
#align simple_graph.chromatic_number_le_one_of_subsingleton SimpleGraph.chromaticNumber_le_one_of_subsingleton
-/

#print SimpleGraph.chromaticNumber_eq_zero_of_isempty /-
theorem chromaticNumber_eq_zero_of_isempty (G : SimpleGraph V) [IsEmpty V] :
    G.chromaticNumber = 0 := by
  rw [← nonpos_iff_eq_zero]
  apply cinfₛ_le chromatic_number_bdd_below
  apply colorable_of_is_empty
#align simple_graph.chromatic_number_eq_zero_of_isempty SimpleGraph.chromaticNumber_eq_zero_of_isempty
-/

#print SimpleGraph.isEmpty_of_chromaticNumber_eq_zero /-
theorem isEmpty_of_chromaticNumber_eq_zero (G : SimpleGraph V) [Finite V]
    (h : G.chromaticNumber = 0) : IsEmpty V :=
  by
  have h' := G.colorable_chromatic_number_of_fintype
  rw [h] at h'
  exact G.is_empty_of_colorable_zero h'
#align simple_graph.is_empty_of_chromatic_number_eq_zero SimpleGraph.isEmpty_of_chromaticNumber_eq_zero
-/

#print SimpleGraph.chromaticNumber_pos /-
theorem chromaticNumber_pos [Nonempty V] {n : ℕ} (hc : G.Colorable n) : 0 < G.chromaticNumber :=
  by
  apply le_cinfₛ (colorable_set_nonempty_of_colorable hc)
  intro m hm
  by_contra h'
  simp only [not_le, Nat.lt_one_iff] at h'
  subst h'
  obtain ⟨i, hi⟩ := hm.some (Classical.arbitrary V)
  exact Nat.not_lt_zero _ hi
#align simple_graph.chromatic_number_pos SimpleGraph.chromaticNumber_pos
-/

#print SimpleGraph.colorable_of_chromaticNumber_pos /-
theorem colorable_of_chromaticNumber_pos (h : 0 < G.chromaticNumber) :
    G.Colorable G.chromaticNumber :=
  by
  obtain ⟨h, hn⟩ := Nat.nonempty_of_pos_infₛ h
  exact colorable_chromatic_number hn
#align simple_graph.colorable_of_chromatic_number_pos SimpleGraph.colorable_of_chromaticNumber_pos
-/

#print SimpleGraph.Colorable.mono_left /-
theorem Colorable.mono_left {G' : SimpleGraph V} (h : G ≤ G') {n : ℕ} (hc : G'.Colorable n) :
    G.Colorable n :=
  ⟨hc.some.comp (Hom.mapSpanningSubgraphs h)⟩
#align simple_graph.colorable.mono_left SimpleGraph.Colorable.mono_left
-/

/- warning: simple_graph.colorable.chromatic_number_le_of_forall_imp -> SimpleGraph.Colorable.chromaticNumber_le_of_forall_imp is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} {G : SimpleGraph.{u1} V} {V' : Type.{u2}} {G' : SimpleGraph.{u2} V'} {m : Nat}, (SimpleGraph.Colorable.{u2} V' G' m) -> (forall (n : Nat), (SimpleGraph.Colorable.{u2} V' G' n) -> (SimpleGraph.Colorable.{u1} V G n)) -> (LE.le.{0} Nat Nat.hasLe (SimpleGraph.chromaticNumber.{u1} V G) (SimpleGraph.chromaticNumber.{u2} V' G'))
but is expected to have type
  forall {V : Type.{u2}} {G : SimpleGraph.{u2} V} {V' : Type.{u1}} {G' : SimpleGraph.{u1} V'} {m : Nat}, (SimpleGraph.Colorable.{u1} V' G' m) -> (forall (n : Nat), (SimpleGraph.Colorable.{u1} V' G' n) -> (SimpleGraph.Colorable.{u2} V G n)) -> (LE.le.{0} Nat instLENat (SimpleGraph.chromaticNumber.{u2} V G) (SimpleGraph.chromaticNumber.{u1} V' G'))
Case conversion may be inaccurate. Consider using '#align simple_graph.colorable.chromatic_number_le_of_forall_imp SimpleGraph.Colorable.chromaticNumber_le_of_forall_impₓ'. -/
theorem Colorable.chromaticNumber_le_of_forall_imp {V' : Type _} {G' : SimpleGraph V'} {m : ℕ}
    (hc : G'.Colorable m) (h : ∀ n, G'.Colorable n → G.Colorable n) :
    G.chromaticNumber ≤ G'.chromaticNumber :=
  by
  apply cinfₛ_le chromatic_number_bdd_below
  apply h
  apply colorable_chromatic_number hc
#align simple_graph.colorable.chromatic_number_le_of_forall_imp SimpleGraph.Colorable.chromaticNumber_le_of_forall_imp

#print SimpleGraph.Colorable.chromaticNumber_mono /-
theorem Colorable.chromaticNumber_mono (G' : SimpleGraph V) {m : ℕ} (hc : G'.Colorable m)
    (h : G ≤ G') : G.chromaticNumber ≤ G'.chromaticNumber :=
  hc.chromaticNumber_le_of_forall_imp fun n => Colorable.mono_left h
#align simple_graph.colorable.chromatic_number_mono SimpleGraph.Colorable.chromaticNumber_mono
-/

/- warning: simple_graph.colorable.chromatic_number_mono_of_embedding -> SimpleGraph.Colorable.chromaticNumber_mono_of_embedding is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} {G : SimpleGraph.{u1} V} {V' : Type.{u2}} {G' : SimpleGraph.{u2} V'} {n : Nat}, (SimpleGraph.Colorable.{u2} V' G' n) -> (SimpleGraph.Embedding.{u1, u2} V V' G G') -> (LE.le.{0} Nat Nat.hasLe (SimpleGraph.chromaticNumber.{u1} V G) (SimpleGraph.chromaticNumber.{u2} V' G'))
but is expected to have type
  forall {V : Type.{u2}} {G : SimpleGraph.{u2} V} {V' : Type.{u1}} {G' : SimpleGraph.{u1} V'} {n : Nat}, (SimpleGraph.Colorable.{u1} V' G' n) -> (SimpleGraph.Embedding.{u2, u1} V V' G G') -> (LE.le.{0} Nat instLENat (SimpleGraph.chromaticNumber.{u2} V G) (SimpleGraph.chromaticNumber.{u1} V' G'))
Case conversion may be inaccurate. Consider using '#align simple_graph.colorable.chromatic_number_mono_of_embedding SimpleGraph.Colorable.chromaticNumber_mono_of_embeddingₓ'. -/
theorem Colorable.chromaticNumber_mono_of_embedding {V' : Type _} {G' : SimpleGraph V'} {n : ℕ}
    (h : G'.Colorable n) (f : G ↪g G') : G.chromaticNumber ≤ G'.chromaticNumber :=
  h.chromaticNumber_le_of_forall_imp fun _ => Colorable.of_embedding f
#align simple_graph.colorable.chromatic_number_mono_of_embedding SimpleGraph.Colorable.chromaticNumber_mono_of_embedding

#print SimpleGraph.chromaticNumber_eq_card_of_forall_surj /-
theorem chromaticNumber_eq_card_of_forall_surj [Fintype α] (C : G.Coloring α)
    (h : ∀ C' : G.Coloring α, Function.Surjective C') : G.chromaticNumber = Fintype.card α :=
  by
  apply le_antisymm
  · apply chromatic_number_le_card C
  · by_contra hc
    rw [not_le] at hc
    obtain ⟨n, cn, hc⟩ :=
      exists_lt_of_cinfₛ_lt (colorable_set_nonempty_of_colorable C.to_colorable) hc
    rw [← Fintype.card_fin n] at hc
    have f := (Function.Embedding.nonempty_of_card_le (le_of_lt hc)).some
    have C' := cn.some
    specialize h (G.recolor_of_embedding f C')
    change Function.Surjective (f ∘ C') at h
    have h1 : Function.Surjective f := Function.Surjective.of_comp h
    have h2 := Fintype.card_le_of_surjective _ h1
    exact Nat.lt_le_antisymm hc h2
#align simple_graph.chromatic_number_eq_card_of_forall_surj SimpleGraph.chromaticNumber_eq_card_of_forall_surj
-/

/- warning: simple_graph.chromatic_number_bot -> SimpleGraph.chromaticNumber_bot is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Nonempty.{succ u1} V], Eq.{1} Nat (SimpleGraph.chromaticNumber.{u1} V (Bot.bot.{u1} (SimpleGraph.{u1} V) (CompleteLattice.toHasBot.{u1} (SimpleGraph.{u1} V) (Order.Coframe.toCompleteLattice.{u1} (SimpleGraph.{u1} V) (CompleteDistribLattice.toCoframe.{u1} (SimpleGraph.{u1} V) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (SimpleGraph.{u1} V) (SimpleGraph.completeBooleanAlgebra.{u1} V))))))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))
but is expected to have type
  forall {V : Type.{u1}} [_inst_1 : Nonempty.{succ u1} V], Eq.{1} Nat (SimpleGraph.chromaticNumber.{u1} V (Bot.bot.{u1} (SimpleGraph.{u1} V) (CompleteLattice.toBot.{u1} (SimpleGraph.{u1} V) (Order.Coframe.toCompleteLattice.{u1} (SimpleGraph.{u1} V) (CompleteDistribLattice.toCoframe.{u1} (SimpleGraph.{u1} V) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (SimpleGraph.{u1} V) (SimpleGraph.completeBooleanAlgebra.{u1} V))))))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))
Case conversion may be inaccurate. Consider using '#align simple_graph.chromatic_number_bot SimpleGraph.chromaticNumber_botₓ'. -/
theorem chromaticNumber_bot [Nonempty V] : (⊥ : SimpleGraph V).chromaticNumber = 1 :=
  by
  let C : (⊥ : SimpleGraph V).Coloring (Fin 1) := coloring.mk (fun _ => 0) fun v w h => False.elim h
  apply le_antisymm
  · exact chromatic_number_le_card C
  · exact chromatic_number_pos C.to_colorable
#align simple_graph.chromatic_number_bot SimpleGraph.chromaticNumber_bot

/- warning: simple_graph.chromatic_number_top -> SimpleGraph.chromaticNumber_top is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Fintype.{u1} V], Eq.{1} Nat (SimpleGraph.chromaticNumber.{u1} V (Top.top.{u1} (SimpleGraph.{u1} V) (CompleteLattice.toHasTop.{u1} (SimpleGraph.{u1} V) (Order.Coframe.toCompleteLattice.{u1} (SimpleGraph.{u1} V) (CompleteDistribLattice.toCoframe.{u1} (SimpleGraph.{u1} V) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (SimpleGraph.{u1} V) (SimpleGraph.completeBooleanAlgebra.{u1} V))))))) (Fintype.card.{u1} V _inst_1)
but is expected to have type
  forall {V : Type.{u1}} [_inst_1 : Fintype.{u1} V], Eq.{1} Nat (SimpleGraph.chromaticNumber.{u1} V (Top.top.{u1} (SimpleGraph.{u1} V) (CompleteLattice.toTop.{u1} (SimpleGraph.{u1} V) (Order.Coframe.toCompleteLattice.{u1} (SimpleGraph.{u1} V) (CompleteDistribLattice.toCoframe.{u1} (SimpleGraph.{u1} V) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (SimpleGraph.{u1} V) (SimpleGraph.completeBooleanAlgebra.{u1} V))))))) (Fintype.card.{u1} V _inst_1)
Case conversion may be inaccurate. Consider using '#align simple_graph.chromatic_number_top SimpleGraph.chromaticNumber_topₓ'. -/
@[simp]
theorem chromaticNumber_top [Fintype V] : (⊤ : SimpleGraph V).chromaticNumber = Fintype.card V :=
  by
  apply chromatic_number_eq_card_of_forall_surj (self_coloring _)
  intro C
  rw [← Finite.injective_iff_surjective]
  intro v w
  contrapose
  intro h
  exact C.valid h
#align simple_graph.chromatic_number_top SimpleGraph.chromaticNumber_top

/- warning: simple_graph.chromatic_number_top_eq_zero_of_infinite -> SimpleGraph.chromaticNumber_top_eq_zero_of_infinite is a dubious translation:
lean 3 declaration is
  forall (V : Type.{u1}) [_inst_1 : Infinite.{succ u1} V], Eq.{1} Nat (SimpleGraph.chromaticNumber.{u1} V (Top.top.{u1} (SimpleGraph.{u1} V) (CompleteLattice.toHasTop.{u1} (SimpleGraph.{u1} V) (Order.Coframe.toCompleteLattice.{u1} (SimpleGraph.{u1} V) (CompleteDistribLattice.toCoframe.{u1} (SimpleGraph.{u1} V) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (SimpleGraph.{u1} V) (SimpleGraph.completeBooleanAlgebra.{u1} V))))))) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))
but is expected to have type
  forall (V : Type.{u1}) [_inst_1 : Infinite.{succ u1} V], Eq.{1} Nat (SimpleGraph.chromaticNumber.{u1} V (Top.top.{u1} (SimpleGraph.{u1} V) (CompleteLattice.toTop.{u1} (SimpleGraph.{u1} V) (Order.Coframe.toCompleteLattice.{u1} (SimpleGraph.{u1} V) (CompleteDistribLattice.toCoframe.{u1} (SimpleGraph.{u1} V) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (SimpleGraph.{u1} V) (SimpleGraph.completeBooleanAlgebra.{u1} V))))))) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))
Case conversion may be inaccurate. Consider using '#align simple_graph.chromatic_number_top_eq_zero_of_infinite SimpleGraph.chromaticNumber_top_eq_zero_of_infiniteₓ'. -/
theorem chromaticNumber_top_eq_zero_of_infinite (V : Type _) [Infinite V] :
    (⊤ : SimpleGraph V).chromaticNumber = 0 :=
  by
  let n := (⊤ : SimpleGraph V).chromaticNumber
  by_contra hc
  replace hc := pos_iff_ne_zero.mpr hc
  apply Nat.not_succ_le_self n
  convert_to(⊤ : SimpleGraph { m | m < n + 1 }).chromaticNumber ≤ _
  · simp
  refine' (colorable_of_chromatic_number_pos hc).chromaticNumber_mono_of_embedding _
  apply embedding.complete_graph
  exact (Function.Embedding.subtype _).trans (Infinite.natEmbedding V)
#align simple_graph.chromatic_number_top_eq_zero_of_infinite SimpleGraph.chromaticNumber_top_eq_zero_of_infinite

#print SimpleGraph.CompleteBipartiteGraph.bicoloring /-
/-- The bicoloring of a complete bipartite graph using whether a vertex
is on the left or on the right. -/
def CompleteBipartiteGraph.bicoloring (V W : Type _) : (completeBipartiteGraph V W).Coloring Bool :=
  Coloring.mk (fun v => v.isRight)
    (by
      intro v w
      cases v <;> cases w <;> simp)
#align simple_graph.complete_bipartite_graph.bicoloring SimpleGraph.CompleteBipartiteGraph.bicoloring
-/

/- warning: simple_graph.complete_bipartite_graph.chromatic_number -> SimpleGraph.CompleteBipartiteGraph.chromaticNumber is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} {W : Type.{u2}} [_inst_1 : Nonempty.{succ u1} V] [_inst_2 : Nonempty.{succ u2} W], Eq.{1} Nat (SimpleGraph.chromaticNumber.{max u1 u2} (Sum.{u1, u2} V W) (completeBipartiteGraph.{u1, u2} V W)) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {V : Type.{u2}} {W : Type.{u1}} [_inst_1 : Nonempty.{succ u2} V] [_inst_2 : Nonempty.{succ u1} W], Eq.{1} Nat (SimpleGraph.chromaticNumber.{max u2 u1} (Sum.{u2, u1} V W) (completeBipartiteGraph.{u2, u1} V W)) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))
Case conversion may be inaccurate. Consider using '#align simple_graph.complete_bipartite_graph.chromatic_number SimpleGraph.CompleteBipartiteGraph.chromaticNumberₓ'. -/
theorem CompleteBipartiteGraph.chromaticNumber {V W : Type _} [Nonempty V] [Nonempty W] :
    (completeBipartiteGraph V W).chromaticNumber = 2 :=
  by
  apply chromatic_number_eq_card_of_forall_surj (complete_bipartite_graph.bicoloring V W)
  intro C b
  have v := Classical.arbitrary V
  have w := Classical.arbitrary W
  have h : (completeBipartiteGraph V W).Adj (Sum.inl v) (Sum.inr w) := by simp
  have hn := C.valid h
  by_cases he : C (Sum.inl v) = b
  · exact ⟨_, he⟩
  · by_cases he' : C (Sum.inr w) = b
    · exact ⟨_, he'⟩
    · exfalso
      cases b <;> simp only [eq_true_eq_not_eq_false, Bool.eq_false_eq_not_eq_true] at he he' <;>
          rw [he, he'] at hn <;>
        contradiction
#align simple_graph.complete_bipartite_graph.chromatic_number SimpleGraph.CompleteBipartiteGraph.chromaticNumber

/-! ### Cliques -/

#print SimpleGraph.IsClique.card_le_of_coloring /-
theorem IsClique.card_le_of_coloring {s : Finset V} (h : G.IsClique s) [Fintype α]
    (C : G.Coloring α) : s.card ≤ Fintype.card α :=
  by
  rw [is_clique_iff_induce_eq] at h
  have f : G.induce ↑s ↪g G := embedding.induce ↑s
  rw [h] at f
  convert Fintype.card_le_of_injective _ (C.comp f.to_hom).injective_of_top_hom using 1
  simp
#align simple_graph.is_clique.card_le_of_coloring SimpleGraph.IsClique.card_le_of_coloring
-/

#print SimpleGraph.IsClique.card_le_of_colorable /-
theorem IsClique.card_le_of_colorable {s : Finset V} (h : G.IsClique s) {n : ℕ}
    (hc : G.Colorable n) : s.card ≤ n :=
  by
  convert h.card_le_of_coloring hc.some
  simp
#align simple_graph.is_clique.card_le_of_colorable SimpleGraph.IsClique.card_le_of_colorable
-/

#print SimpleGraph.IsClique.card_le_chromaticNumber /-
-- TODO eliminate `finite V` constraint once chromatic numbers are refactored.
-- This is just to ensure the chromatic number exists.
theorem IsClique.card_le_chromaticNumber [Finite V] {s : Finset V} (h : G.IsClique s) :
    s.card ≤ G.chromaticNumber := by
  cases nonempty_fintype V
  exact h.card_le_of_colorable G.colorable_chromatic_number_of_fintype
#align simple_graph.is_clique.card_le_chromatic_number SimpleGraph.IsClique.card_le_chromaticNumber
-/

#print SimpleGraph.Colorable.cliqueFree /-
protected theorem Colorable.cliqueFree {n m : ℕ} (hc : G.Colorable n) (hm : n < m) :
    G.CliqueFree m := by
  by_contra h
  simp only [clique_free, is_n_clique_iff, not_forall, Classical.not_not] at h
  obtain ⟨s, h, rfl⟩ := h
  exact Nat.lt_le_antisymm hm (h.card_le_of_colorable hc)
#align simple_graph.colorable.clique_free SimpleGraph.Colorable.cliqueFree
-/

#print SimpleGraph.cliqueFree_of_chromaticNumber_lt /-
-- TODO eliminate `finite V` constraint once chromatic numbers are refactored.
-- This is just to ensure the chromatic number exists.
theorem cliqueFree_of_chromaticNumber_lt [Finite V] {n : ℕ} (hc : G.chromaticNumber < n) :
    G.CliqueFree n :=
  G.colorable_chromaticNumber_of_fintype.CliqueFree hc
#align simple_graph.clique_free_of_chromatic_number_lt SimpleGraph.cliqueFree_of_chromaticNumber_lt
-/

end SimpleGraph


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import algebra.algebra.basic
import algebra.star.basic

/-!
# Star algebras

Introduces the notion of a star algebra over a star ring.
-/

universes u v

/--
A star algebra `A` over a star ring `R` is an algebra which is a star ring,
and the two star structures are compatible in the sense
`star (r • a) = star r • star a`.
-/
-- Note that we take `star_ring A` as a typeclass argument, rather than extending it,
-- to avoid having multiple definitions of the star operation.
class star_algebra (R : Type u) (A : Type v)
  [comm_semiring R] [star_ring R] [semiring A] [star_ring A] [algebra R A] :=
(star_smul : ∀ (r : R) (a : A), star (r • a) = (@has_star.star R _ r) • star a)

@[simp] lemma star_smul {R : Type u} {A : Type v}
  [comm_semiring R] [star_ring R] [semiring A] [star_ring A] [algebra R A] [star_algebra R A]
  (r : R) (a : A) :
  star (r • a) = star r • star a :=
star_algebra.star_smul r a


/-
  We show that R[1/S(D(f))] ≅ R[1/f]. Where S(U) := { r | U ⊆ D(r) }
-/

import to_mathlib.localization.localization_alt
import spectrum_of_a_ring.structure_presheaf

universe u

local attribute [instance] classical.prop_decidable

noncomputable theory

variables {R : Type u} [comm_ring R]

open topological_space
open classical
open localization
open localization_alt

section localization_property

-- Proof of the localization property. R[1/S(D(f))] ≃ R[1/f].

variables {U : opens (Spec R)} (BU : U ∈ D_fs R)

lemma structure_presheaf.inverts_data
: inverts_data
    (powers (some BU))
    (of : R → localization R (S U)) :=
begin
  rintros ⟨s, Hs⟩,
  have HsS : s ∈ S (Spec.DO R (classical.some BU))
    := (is_submonoid.power_subset (S.f_mem (classical.some BU))) Hs,
  rw ←classical.some_spec BU at HsS,
  use [⟦⟨1, ⟨s, HsS⟩⟩⟧],
  apply quotient.sound,
  use [1, is_submonoid.one_mem _],
  simp,
end

lemma structure_presheaf.has_denom_data
: has_denom_data
    (powers (some BU))
    (of : R → localization R (S U)) :=
begin
  intros x,
  have Hx := quotient.exists_rep x,
  rcases (classical.indefinite_description _ Hx) with ⟨⟨p, q⟩, Hpq⟩,
  rcases q with ⟨q, Hq⟩,
  change U.val ⊆ Spec.D'(q) at Hq,
  rw classical.some_spec BU at Hq,
  have Hea := pow_eq.of_Dfs_subset Hq,
  rcases (classical.indefinite_description _ Hea) with ⟨a, He⟩,
  rcases (classical.indefinite_description _ He) with ⟨e, Hfe⟩,
  use [⟨⟨(classical.some BU)^e, ⟨e, rfl⟩⟩, a * p⟩],
  dsimp only [subtype.coe_mk],
  rw [Hfe, ←Hpq],
  apply quotient.sound,
  use [1, is_submonoid.one_mem _],
  dsimp,
  ring,
end

lemma structure_presheaf.ker_le
: ker (of : R → localization R (S U)) ≤ submonoid_ann (powers (some BU)) :=
begin
  intros x Hx,
  change localization.of x = 0 at Hx,
  erw quotient.eq at Hx,
  rcases Hx with ⟨s, ⟨Hs, Hx⟩⟩,
  simp at Hx,
  dsimp [S] at Hs,
  change U.val ⊆ Spec.D'(s) at Hs,
  rw classical.some_spec BU at Hs,
  have Hea := pow_eq.of_Dfs_subset Hs,
  rcases (classical.indefinite_description _ Hea) with ⟨a, He⟩,
  rcases (classical.indefinite_description _ He) with ⟨e, Hfe⟩,
  use [⟨x, ⟨(some BU)^e, ⟨e, rfl⟩⟩⟩],
  { dsimp only [subtype.coe_mk],
    rw [Hfe, mul_comm a, ←mul_assoc, Hx, zero_mul], },
  { refl, }
end

lemma structure_presheaf.localization
: is_localization_data
    (powers (some BU))
    (of : R → localization R (S U)) :=
{ inverts := structure_presheaf.inverts_data BU,
  has_denom := structure_presheaf.has_denom_data BU,
  ker_le := structure_presheaf.ker_le BU }

end localization_property