import .lovelib

/- # LoVe Exercise 3: Forward Proofs -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/- ## Question 1: Connectives and Quantifiers

1.1. Supply structured proofs of the following lemmas. -/

lemma I (a : Prop) :
  a → a :=
assume ha : a,
show a, from
  ha

lemma K (a b : Prop) :
  a → b → b :=
assume ha : a,
assume hb : b,
show b, from
  hb

lemma C (a b c : Prop) :
  (a → b → c) → b → a → c :=
assume hg : a → b → c,
assume hb : b,
assume ha : a,
show c, from
  hg ha hb

lemma proj_1st (a : Prop) :
  a → a → a :=
assume ha ha' : a,
show a, from
  ha

/- Please give a different answer than for `proj_1st`. -/

lemma proj_2nd (a : Prop) :
  a → a → a :=
assume ha ha' : a,
show a, from
  ha'

lemma some_nonsense (a b c : Prop) :
  (a → b → c) → a → (a → c) → b → c :=
assume hg : a → b → c,
assume ha : a,
assume hf : a → c,
assume hb : b,
have hc : c :=
  hf ha,
show c, from
  hc

/- 1.2. Supply a structured proof of the contraposition rule. -/

lemma contrapositive (a b : Prop) :
  (a → b) → ¬ b → ¬ a :=
assume hab : a → b,
assume hnb : ¬ b,
assume ha : a,
have hb : b :=
  hab ha,
show false, from
  hnb hb

/- 1.3. Supply a structured proof of the distributivity of `∀` over `∧`. -/

lemma forall_and {α : Type} (p q : α → Prop) :
  (∀x, p x ∧ q x) ↔ (∀x, p x) ∧ (∀x, q x) :=
iff.intro
  (assume hpq : ∀x, p x ∧ q x,
   have hp : ∀x, p x :=
     fix x,
     and.elim_left (hpq x),
   have hq : ∀x, q x :=
     fix x,
     and.elim_right (hpq x),
   show (∀x, p x) ∧ (∀x, q x), from
     and.intro hp hq)
  (assume hpq : (∀x, p x) ∧ (∀x, q x),
   have hp : ∀x, p x :=
     and.elim_left hpq,
   have hq : ∀x, q x :=
     and.elim_right hpq,
   assume x : α,
   show p x ∧ q x, from
     and.intro (hp x) (hq x))

/- 1.4. Reuse, if possible, the lemma `forall_and` you proved above to prove
the following instance of the lemma. -/

lemma forall_and_inst {α : Type} (r s : α → α → Prop) :
  (∀x, r x x ∧ s x x) ↔ (∀x, r x x) ∧ (∀x, s x x) :=
forall_and (λx, r x x) (λx, s x x)

/- ## Question 2: Chain of Equalities

2.1. Write the following proof using `calc`.

      `(a + b) * (a + b)`
    `= a * (a + b) + b * (a + b)`
    `= a * a + a * b + b * a + b * b`
    `= a * a + a * b + a * b + b * b`
    `= a * a + 2 * a * b + b * b`

Hint: You might need the tactics `simp` and `cc` and the lemmas `mul_add`,
`add_mul`, and `two_mul`. -/

lemma binomial_square (a b : ℕ) :
  (a + b) * (a + b) = a * a + 2 * a * b + b * b :=
calc  (a + b) * (a + b)
    = a * (a + b) + b * (a + b) :
  begin
    simp [add_mul, mul_add],
    cc
  end
... = a * a + a * b + b * a + b * b :
  begin
    simp [add_mul, mul_add],
    cc
  end
... = a * a + a * b + a * b + b * b :
  by cc
... = a * a + 2 * a * b + b * b :
  begin
    simp [two_mul, mul_add, add_mul],
    cc
  end

/- 2.2. Prove the same argument again, this time as a structured proof. Try to
reuse as much of the above proof idea as possible. -/

lemma binomial_square₂ (a b : ℕ) :
  (a + b) * (a + b) = a * a + 2 * a * b + b * b :=
have h1 : (a + b) * (a + b) = a * (a + b) + b * (a + b) :=
  begin
    simp [add_mul, mul_add],
    cc
  end,
have h2 : a * (a + b) + b * (a + b) = a * a + a * b + b * a + b * b :=
  begin
    simp [add_mul, mul_add],
    cc
  end,
have h3 : a * a + a * b + b * a + b * b = a * a + a * b + a * b + b * b :=
  begin
    simp,
    cc
  end,
have h4 : a * a + a * b + a * b + b * b = a * a + 2 * a * b + b * b :=
  begin
    simp [two_mul, add_mul, mul_add],
    cc
  end,
show _, from
  begin
    rw h1,
    rw h2,
    rw h3,
    rw h4
  end

/- 2.3. Prove the same lemma again, this time using tactics. -/

lemma binomial_square₃ (a b : ℕ) :
  (a + b) * (a + b) = a * a + 2 * a * b + b * b :=
begin
  simp [two_mul, add_mul, mul_add],
  cc
end

/- ## Question 3 (**optional**): One-Point Rules

3.1 (**optional**). Prove that the following wrong formulation of the one-point
rule for `∀` is inconsistent, using a structured proof. -/

axiom forall.one_point_wrong {α : Type} {t : α} {p : α → Prop} :
  (∀x : α, x = t ∧ p x) ↔ p t

lemma proof_of_false :
  false :=
begin
  have wrong : (∀x, x = 0 ∧ true) ↔ true :=
    @forall.one_point_wrong ℕ 0 (λ_, true),
  simp at wrong,
  have one_eq_zero : 1 = 0 :=
    wrong 1,
  cc
end

/- 3.2 (**optional**). Prove that the following wrong formulation of the
one-point rule for `∃` is inconsistent, using a tactical or structured proof. -/

axiom exists.one_point_wrong {α : Type} {t : α} {p : α → Prop} :
  (∃x : α, x = t → p x) ↔ p t

lemma proof_of_false₂ :
  false :=
begin
  have wrong : (∃x, x ≠ 0) ↔ false :=
    @exists.one_point_wrong ℕ 0 (λ_, false),
  simp at wrong,
  have one_eq_zero : 1 = 0 :=
    wrong 1,
  cc
end

end LoVe


import logic.function
import data.fintype

def list.chain'' {α} (R : α → α → Prop) : (α → Prop) → list α → α → Prop
| P [] a := P a
| P (a::l) b := P a ∧ list.chain'' (R a) l b

def flip_one {α} [decidable_eq α] (f : α → bool) (i : α) : α → bool :=
function.update f i (bnot (f i))

def admissible {α} [decidable_eq α] (f g : α → bool) : Prop :=
∃ i, g = flip_one f i

def restricted_admissible {α} [decidable_eq α] (f g : α ⊕ α → bool) : Prop :=
∃ i, g = flip_one f (sum.inl i)

def end_state {α} : α ⊕ α → bool
| (sum.inl _) := tt
| (sum.inr _) := ff

def lamp_seq {α} (R : ∀ (f g : α ⊕ α → bool), Prop) (l : list (α ⊕ α → bool)) : Prop :=
list.chain'' R (λ s, end_state = s) l (λ _, ff)

open_locale classical
theorem C4 {α} [fintype α] [decidable_eq α] (k n : ℕ)
  (h1 : 2 ∣ k + n) (h2 : n ≤ k) (h3 : fintype.card α = n) :
  fintype.card {f : vector (α ⊕ α → bool) k // lamp_seq admissible f.1} =
  2 ^ (k - n) *
  fintype.card {f : vector (α ⊕ α → bool) k // lamp_seq restricted_admissible f.1} :=
sorry


/-
Copyright (c) 2020 Fox Thomson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Fox Thomson.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.finset.basic
import Mathlib.PostPort

universes u_1 u v

namespace Mathlib

/-!
# Languages
This file contains the definition and operations on formal languages over an alphabet. Note strings
are implemented as lists over the alphabet.
The operations in this file define a [Kleene algebra](https://en.wikipedia.org/wiki/Kleene_algebra)
over the languages.
-/

/-- A language is a set of strings over an alphabet. -/
def language (α : Type u_1) := set (List α)

namespace language

protected instance has_zero {α : Type u} : HasZero (language α) := { zero := ∅ }

protected instance has_one {α : Type u} : HasOne (language α) := { one := singleton [] }

protected instance inhabited {α : Type u} : Inhabited (language α) := { default := 0 }

protected instance has_add {α : Type u} : Add (language α) := { add := set.union }

protected instance has_mul {α : Type u} : Mul (language α) :=
  { mul :=
      fun (l m : language α) =>
        (fun (p : List α × List α) => prod.fst p ++ prod.snd p) '' set.prod l m }

theorem zero_def {α : Type u} : 0 = ∅ := rfl

theorem one_def {α : Type u} : 1 = singleton [] := rfl

theorem add_def {α : Type u} (l : language α) (m : language α) : l + m = l ∪ m := rfl

theorem mul_def {α : Type u} (l : language α) (m : language α) :
    l * m = (fun (p : List α × List α) => prod.fst p ++ prod.snd p) '' set.prod l m :=
  rfl

/-- The star of a language `L` is the set of all strings which can be written by concatenating
  strings from `L`. -/
def star {α : Type u} (l : language α) : language α :=
  set_of fun (x : List α) => ∃ (S : List (List α)), x = list.join S ∧ ∀ (y : List α), y ∈ S → y ∈ l

theorem star_def {α : Type u} (l : language α) :
    star l =
        set_of
          fun (x : List α) =>
            ∃ (S : List (List α)), x = list.join S ∧ ∀ (y : List α), y ∈ S → y ∈ l :=
  rfl

@[simp] theorem mem_one {α : Type u} (x : List α) : x ∈ 1 ↔ x = [] := iff.refl (x ∈ 1)

@[simp] theorem mem_add {α : Type u} (l : language α) (m : language α) (x : List α) :
    x ∈ l + m ↔ x ∈ l ∨ x ∈ m :=
  sorry

theorem mem_mul {α : Type u} (l : language α) (m : language α) (x : List α) :
    x ∈ l * m ↔ ∃ (a : List α), ∃ (b : List α), a ∈ l ∧ b ∈ m ∧ a ++ b = x :=
  sorry

theorem mem_star {α : Type u} (l : language α) (x : List α) :
    x ∈ star l ↔ ∃ (S : List (List α)), x = list.join S ∧ ∀ (y : List α), y ∈ S → y ∈ l :=
  iff.refl (x ∈ star l)

protected instance semiring {α : Type u} : semiring (language α) :=
  semiring.mk Add.add sorry 0 sorry sorry sorry Mul.mul mul_assoc_lang 1 one_mul_lang mul_one_lang
    sorry sorry left_distrib_lang right_distrib_lang

@[simp] theorem add_self {α : Type u} (l : language α) : l + l = l := sup_idem

theorem star_def_nonempty {α : Type u} (l : language α) :
    star l =
        set_of
          fun (x : List α) =>
            ∃ (S : List (List α)), x = list.join S ∧ ∀ (y : List α), y ∈ S → y ∈ l ∧ y ≠ [] :=
  sorry

theorem le_iff {α : Type u} (l : language α) (m : language α) : l ≤ m ↔ l + m = m :=
  iff.symm sup_eq_right

theorem le_mul_congr {α : Type u} {l₁ : language α} {l₂ : language α} {m₁ : language α}
    {m₂ : language α} : l₁ ≤ m₁ → l₂ ≤ m₂ → l₁ * l₂ ≤ m₁ * m₂ :=
  sorry

theorem le_add_congr {α : Type u} {l₁ : language α} {l₂ : language α} {m₁ : language α}
    {m₂ : language α} : l₁ ≤ m₁ → l₂ ≤ m₂ → l₁ + l₂ ≤ m₁ + m₂ :=
  sup_le_sup

theorem supr_mul {α : Type u} {ι : Sort v} (l : ι → language α) (m : language α) :
    (supr fun (i : ι) => l i) * m = supr fun (i : ι) => l i * m :=
  sorry

theorem mul_supr {α : Type u} {ι : Sort v} (l : ι → language α) (m : language α) :
    (m * supr fun (i : ι) => l i) = supr fun (i : ι) => m * l i :=
  sorry

theorem supr_add {α : Type u} {ι : Sort v} [Nonempty ι] (l : ι → language α) (m : language α) :
    (supr fun (i : ι) => l i) + m = supr fun (i : ι) => l i + m :=
  supr_sup

theorem add_supr {α : Type u} {ι : Sort v} [Nonempty ι] (l : ι → language α) (m : language α) :
    (m + supr fun (i : ι) => l i) = supr fun (i : ι) => m + l i :=
  sup_supr

theorem star_eq_supr_pow {α : Type u} (l : language α) : star l = supr fun (i : ℕ) => l ^ i := sorry

theorem mul_self_star_comm {α : Type u} (l : language α) : star l * l = l * star l := sorry

@[simp] theorem one_add_self_mul_star_eq_star {α : Type u} (l : language α) :
    1 + l * star l = star l :=
  sorry

@[simp] theorem one_add_star_mul_self_eq_star {α : Type u} (l : language α) :
    1 + star l * l = star l :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 + star l * l = star l)) (mul_self_star_comm l)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1 + l * star l = star l)) (one_add_self_mul_star_eq_star l)))
      (Eq.refl (star l)))

theorem star_mul_le_right_of_mul_le_right {α : Type u} (l : language α) (m : language α) :
    l * m ≤ m → star l * m ≤ m :=
  sorry

theorem star_mul_le_left_of_mul_le_left {α : Type u} (l : language α) (m : language α) :
    m * l ≤ m → m * star l ≤ m :=
  sorry

end Mathlib


------------------------------------------------
------------------------------------------------
-- Dealing with finite, simple graphs.  From the ground up. --
------------------------------------------------
------------------------------------------------
import combinatorics.simple_graph.basic
import combinatorics.simple_graph.degree_sum
import combinatorics.simple_graph.hasse -- path graphs
import combinatorics.simple_graph.partition -- bipartite graphs
.

open_locale big_operators -- enable ∑ notation
open simple_graph

set_option pp.implicit true

universes u
variables {V : Type u}
          --{G : simple_graph V}  -- the graph is simple
          --[fintype V]           -- the graph is finite (necessary for vertex set cardinality to be computed)
          --[decidable_rel G.adj] -- whether two vertices are adjacent is decidable (necessary for vertex degree to be computed)
          --[decidable_eq V]      -- whether two vertices are equal is decidable (necessary for edge_set cardinality to be computed)

------------------------------------------------
-- Setting up shorthand notations. --
------------------------------------------------
notation  X `[G]` := @finset.univ X _   -- V[G]
notation `E[` X `]`   := X.edge_finset -- E[G]
notation `∣∣` X `∣∣`  := X.card         -- ∣∣(V[G])∣∣ or ∣∣E[G]∣∣

------------------------------------------------
-- Tag allowable graph theory theorems --
------------------------------------------------
@[user_attribute]
meta def graph_theory_attr : user_attribute :=
{ name := `graph_theory,
  descr := "A tag for all allowable graph_theory theorems our machine can use." }

------------------------------------------------
-- The allowable theorems --
------------------------------------------------

-- In a simple graph, a vertex connects to at most (n-1) other vertices --
@[graph_theory]
theorem degree_bound (G : simple_graph V) [fintype V] [decidable_rel G.adj] [decidable_eq V]:
  ∀ v : V, G.degree v ≤ ∣∣(V[G])∣∣ - 1 :=
begin
  intros v,
  have := simple_graph.degree_lt_card_verts G v,
  rw finset.card_univ, apply nat.le_pred_of_lt, assumption,
end

-- The degree-sum formula: the sum of the degrees = twice the number of edges --
@[graph_theory]
theorem degree_sum (G : simple_graph V) [fintype V] [decidable_rel G.adj] [decidable_eq V]:
  ∑v,  G.degree v = 2 * ∣∣E[G]∣∣ :=
begin
  apply sum_degrees_eq_twice_card_edges G,
end

-- Handshaking lemma : the sum of degrees is even --
--@[graph_theory]  -- untagged so "hammer" can reason through it on its own
theorem degree_sum_even (G : simple_graph V) [fintype V] [decidable_rel G.adj] [decidable_eq V]:
  even (∑v,  G.degree v) :=
begin
  rw degree_sum, simp,
end

-- Graphs have at most (n choose 2) edges --
--@[graph_theory]  -- untagged so "hammer" can reason through it on its own
theorem edge_bound (G : simple_graph V) [fintype V] [decidable_rel G.adj] [decidable_eq V]:
  ∣∣E[G]∣∣ ≤ ∣∣(V[G])∣∣.choose 2 :=
begin
  have ds := degree_sum G, -- syntactic match to this theorem, so lets add it to hypothesis
  have db := degree_bound G, -- syntactic match to this theorem, so lets add it to hypothesis
  rw nat.choose_two_right, -- expand out "choose", since there is nothing else involving "choose" in hypotheses or library

  -- we HAVE something to do with |E| =  something to do with degree
  -- then we HAVE something to do with degree <= something to do with |V|
  -- and we WANT something to do with |E| <= something to do with |V|
  -- so the steps should be clear, abstractly, in the "quotient graph"

  -- first isolate |E|, applying that it has something to do with degree
  replace ds : G.edge_finset.card = (∑v,  G.degree v) / 2 := by {rw ds, simp},
  rw ds, clear ds,-- rewrite the goal using that |E|

 -- then apply that degree can be bounded by something to do with |V|
  replace db : ∀ (v : V), v ∈ (V[G]) → G.degree v ≤ ∣∣(V[G])∣∣ - 1 := by {intros v h, exact db v},
  have h := finset.sum_le_sum db, dsimp at h, rw finset.sum_const at h, -- apply this theorem to get a closer syntactic match to the goal
  apply nat.div_le_div_right, exact h, -- tidy to get a closer syntactic match to the goal

end

-- Every path is bipartite --
-- @[graph_theory]
-- def is_bipartite (G : simple_graph V) := G.partitionable 2
-- theorem path_is_bipartite (n : ℕ) : ∀n : ℕ, is_bipartite (path_graph n) :=
-- begin
--   intro n,
--   rw is_bipartite,
--   rw partitionable,
--   -- the odd-indexed vertices go in one partition
--   -- the even-indexed vertices go in the other
-- end


/-
Copyright (c) 2022 Alena Gusakov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alena Gusakov
-/
import data.sym.sym2
import combinatorics.simple_graph.basic
import combinatorics.simple_graph.subgraph
import combinatorics.simple_graph.connectivity
import data.list
/-!

# Edge Connectivity

In a simple graph, blah blah

* for edges as first-class objects, maybe we can have a nontrivial type thing on V to specify when there are at least 2 vertices?
* I think that would make things easier.

-/

universes u v

namespace simple_graph
variables {V : Type u} {V' : Type v} [decidable_eq V] [fintype V] [nontrivial V]
variables (G : simple_graph V) (G' : simple_graph V') [decidable_rel G.adj]

/--
A graph G is k-edge-connected if G−F is connected for every F⊆E(G) where |F|< k.
-/
def is_k_edge_connected (k : ℕ) : Prop := ∀ (F : finset (sym2 V)), F ⊆ G.edge_finset → F.card < k →
  connected (G.delete_edges F)

/--
A graph G that is k-edge-connected where 0 < k is connected.
-/
lemma k_edge_connected_is_connected (k : ℕ) (h : 0 < k) (h2 : G.is_k_edge_connected k) : G.connected :=
begin
  unfold is_k_edge_connected at h2,
  specialize h2 ∅,
  simp at h2,
  specialize h2 h,
  exact h2,
end

lemma min_deg_ne_zero_of_connected [nontrivial V] (h2 : 1 < fintype.card V) : G.connected ↔ G.min_degree ≠ 0 :=
begin
  have hv := G.exists_minimal_degree_vertex,
  cases hv with v hv,
  rw fintype.one_lt_card_iff at h2,
  rcases h2 with ⟨a, ⟨b, hb⟩⟩,

  sorry,
end

-- exists_eq_cons_of_ne?
lemma delete_incident_edges_disconnected (u v : V) (h : u ≠ v) : ¬ (G.delete_edges (G.incidence_finset u)).reachable u v :=
begin
  unfold reachable,
  simp only [not_nonempty_iff],
  fconstructor,
  intros p,
  have w := p.get_vert 1,
  have h2 := walk.adj_get_vert_succ p,
  have h3 : 0 < p.length,
  by_contra h4,
  push_neg at h4,
  simp at h4,
  have h5 := walk.eq_of_length_eq_zero h4,
  apply h,
  exact h5,
  specialize h2 h3,
  rw walk.get_vert_zero at h2,
  rw delete_edges_adj at h2,
  cases h2 with h5 h6,
  apply h6,
  simp at *,
  exact h5,
end

lemma delete_incident_edges_not_preconnected (u : V) (h : 0 < G.degree u) : ¬ (G.delete_edges (G.incidence_finset u)).preconnected :=
begin
  unfold preconnected,
  push_neg,
  use u,
  have h3 := G.degree_pos_iff_exists_adj u,
  cases h3 with h3 h5,
  specialize h3 h,
  cases h3 with w hw,
  use w,
  apply delete_incident_edges_disconnected,
  apply G.ne_of_adj hw,
end

lemma edge_conn_le_min_deg (k : ℕ) (h2 : G.is_k_edge_connected k) : k ≤ G.min_degree :=
begin
  have hv := G.exists_minimal_degree_vertex,
  cases hv with v hv,
  have h : ¬ connected (G.delete_edges (G.incidence_finset v)),
  -- v has no neighbors when you delete its incidence set
  cases (G.min_degree),
  {
    by_contra h3,
    sorry },
  { -- unfold degree at hv,
    have h3 := G.degree_pos_iff_exists_adj v,
    cases h3 with h3 h5,
    specialize h3 sorry,
    cases h3 with w hw,
    by_contra h4,
    cases h4 with h4 h5,
    apply G.delete_incident_edges_not_preconnected v sorry,
    exact h4 },
  specialize h2 (G.incidence_finset v),
  specialize h2 sorry,
  contrapose h2,
  push_neg at h2,
  push_neg,
  rw ← G.card_incidence_finset_eq_degree at hv,
  rw hv at h2,
  exact ⟨h2, h⟩,
end

/--
For a set S ⊆ V(G), the cut induced by S (or just a cut) is the set of all edges with one end in S
and one end not in S, denoted by cut G(S) or cut(S).
-/
def edge_cut (S : set V) : set (sym2 V) := {e ∈ G.edge_set | ∃ (v : V) (h : v ∈ e), v ∈ S ∧ sym2.mem.other h ∉ S}
-- why did i have to specify sym2.mem in order to make it work? i can't find the namespace declaration

/--
Lemma 2.6 (Cut criterion for connectivity, Math 239). A graph is connected if and only if every nontrivial cut is nonempty
-/
lemma cut_criterion : G.connected ↔ ∀ (S : set V), set.nonempty S ∧ S ≠ set.univ → set.nonempty (G.edge_cut S) :=
begin
  split,
  { rintros ⟨hpre, hnon⟩ S ⟨hne, hna⟩,
    cases hne with v hv,
    rw set.ne_univ_iff_exists_not_mem at hna,
    cases hna with w hna,
    specialize hpre v w,
    cases hpre with w,
    -- need to show that at some point there is an edge in w that has an endpoint in S and another endpoint not in S
    have h : ∃ e ∈ w.edges, e ∈ G.edge_cut S,
    { unfold edge_cut,
      simp,
      sorry },
    rcases h with ⟨e, ⟨he, he2⟩⟩,
    use ⟨e, he2⟩ },
  { rintros h,
    split,
    intros v w,
    --specialize h {v} sorry,
    sorry },
end

end simple_graph


/-
Copyright (c) 2014 Robert Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Lewis, Leonardo de Moura, Mario Carneiro, Floris van Doorn
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.ordered_ring
import Mathlib.algebra.field
import Mathlib.tactic.monotonicity.basic
import Mathlib.PostPort

universes u_2 l u_1

namespace Mathlib

/-!
  ### Linear ordered fields
  A linear ordered field is a field equipped with a linear order such that
  * addition respects the order: `a ≤ b → c + a ≤ c + b`;
  * multiplication of positives is positive: `0 < a → 0 < b → 0 < a * b`;
  * `0 < 1`.

  ### Main Definitions
  * `linear_ordered_field`: the class of linear ordered fields.
-/

/-- A linear ordered field is a field with a linear order respecting the operations. -/
class linear_ordered_field (α : Type u_2)
extends linear_ordered_comm_ring α, field α
where

/-!
### Lemmas about pos, nonneg, nonpos, neg
-/

@[simp] theorem inv_pos {α : Type u_1} [linear_ordered_field α] {a : α} : 0 < (a⁻¹) ↔ 0 < a := sorry

@[simp] theorem inv_nonneg {α : Type u_1} [linear_ordered_field α] {a : α} : 0 ≤ (a⁻¹) ↔ 0 ≤ a := sorry

@[simp] theorem inv_lt_zero {α : Type u_1} [linear_ordered_field α] {a : α} : a⁻¹ < 0 ↔ a < 0 := sorry

@[simp] theorem inv_nonpos {α : Type u_1} [linear_ordered_field α] {a : α} : a⁻¹ ≤ 0 ↔ a ≤ 0 := sorry

theorem one_div_pos {α : Type u_1} [linear_ordered_field α] {a : α} : 0 < 1 / a ↔ 0 < a :=
  inv_eq_one_div a ▸ inv_pos

theorem one_div_neg {α : Type u_1} [linear_ordered_field α] {a : α} : 1 / a < 0 ↔ a < 0 :=
  inv_eq_one_div a ▸ inv_lt_zero

theorem one_div_nonneg {α : Type u_1} [linear_ordered_field α] {a : α} : 0 ≤ 1 / a ↔ 0 ≤ a :=
  inv_eq_one_div a ▸ inv_nonneg

theorem one_div_nonpos {α : Type u_1} [linear_ordered_field α] {a : α} : 1 / a ≤ 0 ↔ a ≤ 0 :=
  inv_eq_one_div a ▸ inv_nonpos

theorem div_pos_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} : 0 < a / b ↔ 0 < a ∧ 0 < b ∨ a < 0 ∧ b < 0 := sorry

theorem div_neg_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} : a / b < 0 ↔ 0 < a ∧ b < 0 ∨ a < 0 ∧ 0 < b := sorry

theorem div_nonneg_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} : 0 ≤ a / b ↔ 0 ≤ a ∧ 0 ≤ b ∨ a ≤ 0 ∧ b ≤ 0 := sorry

theorem div_nonpos_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} : a / b ≤ 0 ↔ 0 ≤ a ∧ b ≤ 0 ∨ a ≤ 0 ∧ 0 ≤ b := sorry

theorem div_pos {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : 0 < a / b :=
  mul_pos ha (iff.mpr inv_pos hb)

theorem div_pos_of_neg_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : 0 < a / b :=
  mul_pos_of_neg_of_neg ha (iff.mpr inv_lt_zero hb)

theorem div_neg_of_neg_of_pos {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : 0 < b) : a / b < 0 :=
  mul_neg_of_neg_of_pos ha (iff.mpr inv_pos hb)

theorem div_neg_of_pos_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : b < 0) : a / b < 0 :=
  mul_neg_of_pos_of_neg ha (iff.mpr inv_lt_zero hb)

theorem div_nonneg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a / b :=
  mul_nonneg ha (iff.mpr inv_nonneg hb)

theorem div_nonneg_of_nonpos {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a ≤ 0) (hb : b ≤ 0) : 0 ≤ a / b :=
  mul_nonneg_of_nonpos_of_nonpos ha (iff.mpr inv_nonpos hb)

theorem div_nonpos_of_nonpos_of_nonneg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a ≤ 0) (hb : 0 ≤ b) : a / b ≤ 0 :=
  mul_nonpos_of_nonpos_of_nonneg ha (iff.mpr inv_nonneg hb)

theorem div_nonpos_of_nonneg_of_nonpos {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 ≤ a) (hb : b ≤ 0) : a / b ≤ 0 :=
  mul_nonpos_of_nonneg_of_nonpos ha (iff.mpr inv_nonpos hb)

/-!
### Relating one division with another term.
-/

theorem le_div_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) : a ≤ b / c ↔ a * c ≤ b := sorry

theorem le_div_iff' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) : a ≤ b / c ↔ c * a ≤ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ b / c ↔ c * a ≤ b)) (mul_comm c a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ b / c ↔ a * c ≤ b)) (propext (le_div_iff hc)))) (iff.refl (a * c ≤ b)))

theorem div_le_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hb : 0 < b) : a / b ≤ c ↔ a ≤ c * b := sorry

theorem div_le_iff' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hb : 0 < b) : a / b ≤ c ↔ a ≤ b * c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b ≤ c ↔ a ≤ b * c)) (mul_comm b c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a / b ≤ c ↔ a ≤ c * b)) (propext (div_le_iff hb)))) (iff.refl (a ≤ c * b)))

theorem lt_div_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) : a < b / c ↔ a * c < b :=
  lt_iff_lt_of_le_iff_le (div_le_iff hc)

theorem lt_div_iff' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) : a < b / c ↔ c * a < b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a < b / c ↔ c * a < b)) (mul_comm c a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < b / c ↔ a * c < b)) (propext (lt_div_iff hc)))) (iff.refl (a * c < b)))

theorem div_lt_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) : b / c < a ↔ b < a * c :=
  lt_iff_lt_of_le_iff_le (le_div_iff hc)

theorem div_lt_iff' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) : b / c < a ↔ b < c * a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b / c < a ↔ b < c * a)) (mul_comm c a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b / c < a ↔ b < a * c)) (propext (div_lt_iff hc)))) (iff.refl (b < a * c)))

theorem inv_mul_le_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (h : 0 < b) : b⁻¹ * a ≤ c ↔ a ≤ b * c := sorry

theorem inv_mul_le_iff' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (h : 0 < b) : b⁻¹ * a ≤ c ↔ a ≤ c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹ * a ≤ c ↔ a ≤ c * b)) (propext (inv_mul_le_iff h))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ b * c ↔ a ≤ c * b)) (mul_comm b c))) (iff.refl (a ≤ c * b)))

theorem mul_inv_le_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (h : 0 < b) : a * (b⁻¹) ≤ c ↔ a ≤ b * c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * (b⁻¹) ≤ c ↔ a ≤ b * c)) (mul_comm a (b⁻¹))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹ * a ≤ c ↔ a ≤ b * c)) (propext (inv_mul_le_iff h)))) (iff.refl (a ≤ b * c)))

theorem mul_inv_le_iff' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (h : 0 < b) : a * (b⁻¹) ≤ c ↔ a ≤ c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * (b⁻¹) ≤ c ↔ a ≤ c * b)) (mul_comm a (b⁻¹))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹ * a ≤ c ↔ a ≤ c * b)) (propext (inv_mul_le_iff' h)))) (iff.refl (a ≤ c * b)))

theorem inv_mul_lt_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (h : 0 < b) : b⁻¹ * a < c ↔ a < b * c := sorry

theorem inv_mul_lt_iff' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (h : 0 < b) : b⁻¹ * a < c ↔ a < c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹ * a < c ↔ a < c * b)) (propext (inv_mul_lt_iff h))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < b * c ↔ a < c * b)) (mul_comm b c))) (iff.refl (a < c * b)))

theorem mul_inv_lt_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (h : 0 < b) : a * (b⁻¹) < c ↔ a < b * c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * (b⁻¹) < c ↔ a < b * c)) (mul_comm a (b⁻¹))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹ * a < c ↔ a < b * c)) (propext (inv_mul_lt_iff h)))) (iff.refl (a < b * c)))

theorem mul_inv_lt_iff' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (h : 0 < b) : a * (b⁻¹) < c ↔ a < c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * (b⁻¹) < c ↔ a < c * b)) (mul_comm a (b⁻¹))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹ * a < c ↔ a < c * b)) (propext (inv_mul_lt_iff' h)))) (iff.refl (a < c * b)))

theorem inv_pos_le_iff_one_le_mul {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) : a⁻¹ ≤ b ↔ 1 ≤ b * a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ ≤ b ↔ 1 ≤ b * a)) (inv_eq_one_div a))) (div_le_iff ha)

theorem inv_pos_le_iff_one_le_mul' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) : a⁻¹ ≤ b ↔ 1 ≤ a * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ ≤ b ↔ 1 ≤ a * b)) (inv_eq_one_div a))) (div_le_iff' ha)

theorem inv_pos_lt_iff_one_lt_mul {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) : a⁻¹ < b ↔ 1 < b * a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ < b ↔ 1 < b * a)) (inv_eq_one_div a))) (div_lt_iff ha)

theorem inv_pos_lt_iff_one_lt_mul' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) : a⁻¹ < b ↔ 1 < a * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ < b ↔ 1 < a * b)) (inv_eq_one_div a))) (div_lt_iff' ha)

theorem div_le_iff_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : b / c ≤ a ↔ a * c ≤ b := sorry

theorem div_le_iff_of_neg' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : b / c ≤ a ↔ c * a ≤ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b / c ≤ a ↔ c * a ≤ b)) (mul_comm c a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b / c ≤ a ↔ a * c ≤ b)) (propext (div_le_iff_of_neg hc)))) (iff.refl (a * c ≤ b)))

theorem le_div_iff_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : a ≤ b / c ↔ b ≤ a * c := sorry

theorem le_div_iff_of_neg' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : a ≤ b / c ↔ b ≤ c * a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ b / c ↔ b ≤ c * a)) (mul_comm c a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ b / c ↔ b ≤ a * c)) (propext (le_div_iff_of_neg hc)))) (iff.refl (b ≤ a * c)))

theorem div_lt_iff_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : b / c < a ↔ a * c < b :=
  lt_iff_lt_of_le_iff_le (le_div_iff_of_neg hc)

theorem div_lt_iff_of_neg' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : b / c < a ↔ c * a < b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b / c < a ↔ c * a < b)) (mul_comm c a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b / c < a ↔ a * c < b)) (propext (div_lt_iff_of_neg hc)))) (iff.refl (a * c < b)))

theorem lt_div_iff_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : a < b / c ↔ b < a * c :=
  lt_iff_lt_of_le_iff_le (div_le_iff_of_neg hc)

theorem lt_div_iff_of_neg' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : a < b / c ↔ b < c * a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a < b / c ↔ b < c * a)) (mul_comm c a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < b / c ↔ b < a * c)) (propext (lt_div_iff_of_neg hc)))) (iff.refl (b < a * c)))

/-- One direction of `div_le_iff` where `b` is allowed to be `0` (but `c` must be nonnegative) -/
theorem div_le_of_nonneg_of_le_mul {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hb : 0 ≤ b) (hc : 0 ≤ c) (h : a ≤ c * b) : a / b ≤ c := sorry

theorem div_le_one_of_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (h : a ≤ b) (hb : 0 ≤ b) : a / b ≤ 1 :=
  div_le_of_nonneg_of_le_mul hb zero_le_one (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ 1 * b)) (one_mul b))) h)

/-!
### Bi-implications of inequalities using inversions
-/

theorem inv_le_inv_of_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (h : a ≤ b) : b⁻¹ ≤ (a⁻¹) := sorry

/-- See `inv_le_inv_of_le` for the implication from right-to-left with one fewer assumption. -/
theorem inv_le_inv {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : a⁻¹ ≤ (b⁻¹) ↔ b ≤ a := sorry

theorem inv_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : a⁻¹ ≤ b ↔ b⁻¹ ≤ a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ ≤ b ↔ b⁻¹ ≤ a)) (Eq.symm (propext (inv_le_inv hb (iff.mpr inv_pos ha))))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹ ≤ (a⁻¹⁻¹) ↔ b⁻¹ ≤ a)) (inv_inv' a))) (iff.refl (b⁻¹ ≤ a)))

theorem le_inv {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : a ≤ (b⁻¹) ↔ b ≤ (a⁻¹) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ (b⁻¹) ↔ b ≤ (a⁻¹))) (Eq.symm (propext (inv_le_inv (iff.mpr inv_pos hb) ha)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹⁻¹ ≤ (a⁻¹) ↔ b ≤ (a⁻¹))) (inv_inv' b))) (iff.refl (b ≤ (a⁻¹))))

theorem inv_lt_inv {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : a⁻¹ < (b⁻¹) ↔ b < a :=
  lt_iff_lt_of_le_iff_le (inv_le_inv hb ha)

theorem inv_lt {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : a⁻¹ < b ↔ b⁻¹ < a :=
  lt_iff_lt_of_le_iff_le (le_inv hb ha)

theorem lt_inv {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : a < (b⁻¹) ↔ b < (a⁻¹) :=
  lt_iff_lt_of_le_iff_le (inv_le hb ha)

theorem inv_le_inv_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : a⁻¹ ≤ (b⁻¹) ↔ b ≤ a := sorry

theorem inv_le_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : a⁻¹ ≤ b ↔ b⁻¹ ≤ a :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (a⁻¹ ≤ b ↔ b⁻¹ ≤ a)) (Eq.symm (propext (inv_le_inv_of_neg hb (iff.mpr inv_lt_zero ha))))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b⁻¹ ≤ (a⁻¹⁻¹) ↔ b⁻¹ ≤ a)) (inv_inv' a))) (iff.refl (b⁻¹ ≤ a)))

theorem le_inv_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : a ≤ (b⁻¹) ↔ b ≤ (a⁻¹) := sorry

theorem inv_lt_inv_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : a⁻¹ < (b⁻¹) ↔ b < a :=
  lt_iff_lt_of_le_iff_le (inv_le_inv_of_neg hb ha)

theorem inv_lt_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : a⁻¹ < b ↔ b⁻¹ < a :=
  lt_iff_lt_of_le_iff_le (le_inv_of_neg hb ha)

theorem lt_inv_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : a < (b⁻¹) ↔ b < (a⁻¹) :=
  lt_iff_lt_of_le_iff_le (inv_le_of_neg hb ha)

theorem inv_lt_one {α : Type u_1} [linear_ordered_field α] {a : α} (ha : 1 < a) : a⁻¹ < 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ < 1)) (propext (inv_lt (has_lt.lt.trans zero_lt_one ha) zero_lt_one))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1⁻¹ < a)) inv_one)) ha)

theorem one_lt_inv {α : Type u_1} [linear_ordered_field α] {a : α} (h₁ : 0 < a) (h₂ : a < 1) : 1 < (a⁻¹) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 < (a⁻¹))) (propext (lt_inv zero_lt_one h₁))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < (1⁻¹))) inv_one)) h₂)

theorem inv_le_one {α : Type u_1} [linear_ordered_field α] {a : α} (ha : 1 ≤ a) : a⁻¹ ≤ 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ ≤ 1)) (propext (inv_le (has_lt.lt.trans_le zero_lt_one ha) zero_lt_one))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1⁻¹ ≤ a)) inv_one)) ha)

theorem one_le_inv {α : Type u_1} [linear_ordered_field α] {a : α} (h₁ : 0 < a) (h₂ : a ≤ 1) : 1 ≤ (a⁻¹) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 ≤ (a⁻¹))) (propext (le_inv zero_lt_one h₁))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ (1⁻¹))) inv_one)) h₂)

theorem inv_lt_one_iff_of_pos {α : Type u_1} [linear_ordered_field α] {a : α} (h₀ : 0 < a) : a⁻¹ < 1 ↔ 1 < a :=
  { mp := fun (h₁ : a⁻¹ < 1) => inv_inv' a ▸ one_lt_inv (iff.mpr inv_pos h₀) h₁, mpr := inv_lt_one }

theorem inv_lt_one_iff {α : Type u_1} [linear_ordered_field α] {a : α} : a⁻¹ < 1 ↔ a ≤ 0 ∨ 1 < a := sorry

theorem one_lt_inv_iff {α : Type u_1} [linear_ordered_field α] {a : α} : 1 < (a⁻¹) ↔ 0 < a ∧ a < 1 := sorry

theorem inv_le_one_iff {α : Type u_1} [linear_ordered_field α] {a : α} : a⁻¹ ≤ 1 ↔ a ≤ 0 ∨ 1 ≤ a := sorry

theorem one_le_inv_iff {α : Type u_1} [linear_ordered_field α] {a : α} : 1 ≤ (a⁻¹) ↔ 0 < a ∧ a ≤ 1 := sorry

/-!
### Relating two divisions.
-/

theorem div_le_div_of_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 ≤ c) (h : a ≤ b) : a / c ≤ b / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / c ≤ b / c)) (div_eq_mul_one_div a c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * (1 / c) ≤ b / c)) (div_eq_mul_one_div b c)))
      (mul_le_mul_of_nonneg_right h (iff.mpr one_div_nonneg hc)))

theorem div_le_div_of_le_left {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (ha : 0 ≤ a) (hc : 0 < c) (h : c ≤ b) : a / b ≤ a / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b ≤ a / c)) (div_eq_mul_inv a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * (b⁻¹) ≤ a / c)) (div_eq_mul_inv a c)))
      (mul_le_mul_of_nonneg_left (iff.mpr (inv_le_inv (has_lt.lt.trans_le hc h) hc) h) ha))

theorem div_le_div_of_le_of_nonneg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hab : a ≤ b) (hc : 0 ≤ c) : a / c ≤ b / c :=
  div_le_div_of_le hc hab

theorem div_le_div_of_nonpos_of_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c ≤ 0) (h : b ≤ a) : a / c ≤ b / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / c ≤ b / c)) (div_eq_mul_one_div a c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * (1 / c) ≤ b / c)) (div_eq_mul_one_div b c)))
      (mul_le_mul_of_nonpos_right h (iff.mpr one_div_nonpos hc)))

theorem div_lt_div_of_lt {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) (h : a < b) : a / c < b / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / c < b / c)) (div_eq_mul_one_div a c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * (1 / c) < b / c)) (div_eq_mul_one_div b c)))
      (mul_lt_mul_of_pos_right h (iff.mpr one_div_pos hc)))

theorem div_lt_div_of_neg_of_lt {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) (h : b < a) : a / c < b / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / c < b / c)) (div_eq_mul_one_div a c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * (1 / c) < b / c)) (div_eq_mul_one_div b c)))
      (mul_lt_mul_of_neg_right h (iff.mpr one_div_neg hc)))

theorem div_le_div_right {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) : a / c ≤ b / c ↔ a ≤ b :=
  { mp := le_imp_le_of_lt_imp_lt (div_lt_div_of_lt hc), mpr := div_le_div_of_le (has_lt.lt.le hc) }

theorem div_le_div_right_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : a / c ≤ b / c ↔ b ≤ a :=
  { mp := le_imp_le_of_lt_imp_lt (div_lt_div_of_neg_of_lt hc), mpr := div_le_div_of_nonpos_of_le (has_lt.lt.le hc) }

theorem div_lt_div_right {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : 0 < c) : a / c < b / c ↔ a < b :=
  lt_iff_lt_of_le_iff_le (div_le_div_right hc)

theorem div_lt_div_right_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hc : c < 0) : a / c < b / c ↔ b < a :=
  lt_iff_lt_of_le_iff_le (div_le_div_right_of_neg hc)

theorem div_lt_div_left {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : a / b < a / c ↔ c < b :=
  iff.trans (mul_lt_mul_left ha) (inv_lt_inv hb hc)

theorem div_le_div_left {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : a / b ≤ a / c ↔ c ≤ b :=
  iff.mpr le_iff_le_iff_lt_iff_lt (div_lt_div_left ha hc hb)

theorem div_lt_div_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (b0 : 0 < b) (d0 : 0 < d) : a / b < c / d ↔ a * d < c * b := sorry

theorem div_le_div_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (b0 : 0 < b) (d0 : 0 < d) : a / b ≤ c / d ↔ a * d ≤ c * b := sorry

theorem div_le_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (hc : 0 ≤ c) (hac : a ≤ c) (hd : 0 < d) (hbd : d ≤ b) : a / b ≤ c / d :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b ≤ c / d)) (propext (div_le_div_iff (has_lt.lt.trans_le hd hbd) hd))))
    (mul_le_mul hac hbd (has_lt.lt.le hd) hc)

theorem div_lt_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (hac : a < c) (hbd : d ≤ b) (c0 : 0 ≤ c) (d0 : 0 < d) : a / b < c / d :=
  iff.mpr (div_lt_div_iff (has_lt.lt.trans_le d0 hbd) d0) (mul_lt_mul hac hbd d0 c0)

theorem div_lt_div' {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (hac : a ≤ c) (hbd : d < b) (c0 : 0 < c) (d0 : 0 < d) : a / b < c / d :=
  iff.mpr (div_lt_div_iff (has_lt.lt.trans d0 hbd) d0) (mul_lt_mul' hac hbd (has_lt.lt.le d0) c0)

theorem div_lt_div_of_lt_left {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} (hb : 0 < b) (h : b < a) (hc : 0 < c) : c / a < c / b :=
  iff.mpr (div_lt_div_left hc (has_lt.lt.trans hb h) hb) h

/-!
### Relating one division and involving `1`
-/

theorem one_le_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : 0 < b) : 1 ≤ a / b ↔ b ≤ a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 ≤ a / b ↔ b ≤ a)) (propext (le_div_iff hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1 * b ≤ a ↔ b ≤ a)) (one_mul b))) (iff.refl (b ≤ a)))

theorem div_le_one {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : 0 < b) : a / b ≤ 1 ↔ a ≤ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b ≤ 1 ↔ a ≤ b)) (propext (div_le_iff hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ 1 * b ↔ a ≤ b)) (one_mul b))) (iff.refl (a ≤ b)))

theorem one_lt_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : 0 < b) : 1 < a / b ↔ b < a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 < a / b ↔ b < a)) (propext (lt_div_iff hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1 * b < a ↔ b < a)) (one_mul b))) (iff.refl (b < a)))

theorem div_lt_one {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : 0 < b) : a / b < 1 ↔ a < b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b < 1 ↔ a < b)) (propext (div_lt_iff hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < 1 * b ↔ a < b)) (one_mul b))) (iff.refl (a < b)))

theorem one_le_div_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : b < 0) : 1 ≤ a / b ↔ a ≤ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 ≤ a / b ↔ a ≤ b)) (propext (le_div_iff_of_neg hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ 1 * b ↔ a ≤ b)) (one_mul b))) (iff.refl (a ≤ b)))

theorem div_le_one_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : b < 0) : a / b ≤ 1 ↔ b ≤ a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b ≤ 1 ↔ b ≤ a)) (propext (div_le_iff_of_neg hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1 * b ≤ a ↔ b ≤ a)) (one_mul b))) (iff.refl (b ≤ a)))

theorem one_lt_div_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : b < 0) : 1 < a / b ↔ a < b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 < a / b ↔ a < b)) (propext (lt_div_iff_of_neg hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < 1 * b ↔ a < b)) (one_mul b))) (iff.refl (a < b)))

theorem div_lt_one_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : b < 0) : a / b < 1 ↔ b < a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b < 1 ↔ b < a)) (propext (div_lt_iff_of_neg hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1 * b < a ↔ b < a)) (one_mul b))) (iff.refl (b < a)))

theorem one_div_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : 1 / a ≤ b ↔ 1 / b ≤ a := sorry

theorem one_div_lt {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : 1 / a < b ↔ 1 / b < a := sorry

theorem le_one_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : a ≤ 1 / b ↔ b ≤ 1 / a := sorry

theorem lt_one_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : a < 1 / b ↔ b < 1 / a := sorry

theorem one_div_le_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : 1 / a ≤ b ↔ 1 / b ≤ a := sorry

theorem one_div_lt_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : 1 / a < b ↔ 1 / b < a := sorry

theorem le_one_div_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : a ≤ 1 / b ↔ b ≤ 1 / a := sorry

theorem lt_one_div_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : a < 1 / b ↔ b < 1 / a := sorry

theorem one_lt_div_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} : 1 < a / b ↔ 0 < b ∧ b < a ∨ b < 0 ∧ a < b := sorry

theorem one_le_div_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} : 1 ≤ a / b ↔ 0 < b ∧ b ≤ a ∨ b < 0 ∧ a ≤ b := sorry

theorem div_lt_one_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} : a / b < 1 ↔ 0 < b ∧ a < b ∨ b = 0 ∨ b < 0 ∧ b < a := sorry

theorem div_le_one_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} : a / b ≤ 1 ↔ 0 < b ∧ a ≤ b ∨ b = 0 ∨ b < 0 ∧ b ≤ a := sorry

/-!
### Relating two divisions, involving `1`
-/

theorem one_div_le_one_div_of_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (h : a ≤ b) : 1 / b ≤ 1 / a := sorry

theorem one_div_lt_one_div_of_lt {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (h : a < b) : 1 / b < 1 / a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 / b < 1 / a)) (propext (lt_div_iff' ha))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * (1 / b) < 1)) (Eq.symm (div_eq_mul_one_div a b))))
      (eq.mpr (id (Eq._oldrec (Eq.refl (a / b < 1)) (propext (div_lt_one (has_lt.lt.trans ha h))))) h))

theorem one_div_le_one_div_of_neg_of_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : b < 0) (h : a ≤ b) : 1 / b ≤ 1 / a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 / b ≤ 1 / a)) (propext (div_le_iff_of_neg' hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * (1 / a) ≤ 1)) (Eq.symm (div_eq_mul_one_div b a))))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b / a ≤ 1)) (propext (div_le_one_of_neg (has_le.le.trans_lt h hb))))) h))

theorem one_div_lt_one_div_of_neg_of_lt {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : b < 0) (h : a < b) : 1 / b < 1 / a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 / b < 1 / a)) (propext (div_lt_iff_of_neg' hb))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * (1 / a) < 1)) (Eq.symm (div_eq_mul_one_div b a))))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b / a < 1)) (propext (div_lt_one_of_neg (has_lt.lt.trans h hb))))) h))

theorem le_of_one_div_le_one_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (h : 1 / a ≤ 1 / b) : b ≤ a :=
  le_imp_le_of_lt_imp_lt (one_div_lt_one_div_of_lt ha) h

theorem lt_of_one_div_lt_one_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (h : 1 / a < 1 / b) : b < a :=
  lt_imp_lt_of_le_imp_le (one_div_le_one_div_of_le ha) h

theorem le_of_neg_of_one_div_le_one_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : b < 0) (h : 1 / a ≤ 1 / b) : b ≤ a :=
  le_imp_le_of_lt_imp_lt (one_div_lt_one_div_of_neg_of_lt hb) h

theorem lt_of_neg_of_one_div_lt_one_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (hb : b < 0) (h : 1 / a < 1 / b) : b < a :=
  lt_imp_lt_of_le_imp_le (one_div_le_one_div_of_neg_of_le hb) h

/-- For the single implications with fewer assumptions, see `one_div_le_one_div_of_le` and
  `le_of_one_div_le_one_div` -/
theorem one_div_le_one_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : 1 / a ≤ 1 / b ↔ b ≤ a :=
  div_le_div_left zero_lt_one ha hb

/-- For the single implications with fewer assumptions, see `one_div_lt_one_div_of_lt` and
  `lt_of_one_div_lt_one_div` -/
theorem one_div_lt_one_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : 0 < a) (hb : 0 < b) : 1 / a < 1 / b ↔ b < a :=
  div_lt_div_left zero_lt_one ha hb

/-- For the single implications with fewer assumptions, see `one_div_lt_one_div_of_neg_of_lt` and
  `lt_of_one_div_lt_one_div` -/
theorem one_div_le_one_div_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : 1 / a ≤ 1 / b ↔ b ≤ a := sorry

/-- For the single implications with fewer assumptions, see `one_div_lt_one_div_of_lt` and
  `lt_of_one_div_lt_one_div` -/
theorem one_div_lt_one_div_of_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (ha : a < 0) (hb : b < 0) : 1 / a < 1 / b ↔ b < a :=
  lt_iff_lt_of_le_iff_le (one_div_le_one_div_of_neg hb ha)

theorem one_lt_one_div {α : Type u_1} [linear_ordered_field α] {a : α} (h1 : 0 < a) (h2 : a < 1) : 1 < 1 / a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 < 1 / a)) (propext (lt_one_div zero_lt_one h1))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < 1 / 1)) one_div_one)) h2)

theorem one_le_one_div {α : Type u_1} [linear_ordered_field α] {a : α} (h1 : 0 < a) (h2 : a ≤ 1) : 1 ≤ 1 / a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 ≤ 1 / a)) (propext (le_one_div zero_lt_one h1))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ 1 / 1)) one_div_one)) h2)

theorem one_div_lt_neg_one {α : Type u_1} [linear_ordered_field α] {a : α} (h1 : a < 0) (h2 : -1 < a) : 1 / a < -1 :=
  (fun (this : 1 / a < 1 / -1) => eq.mp (Eq._oldrec (Eq.refl (1 / a < 1 / -1)) one_div_neg_one_eq_neg_one) this)
    (one_div_lt_one_div_of_neg_of_lt h1 h2)

theorem one_div_le_neg_one {α : Type u_1} [linear_ordered_field α] {a : α} (h1 : a < 0) (h2 : -1 ≤ a) : 1 / a ≤ -1 :=
  (fun (this : 1 / a ≤ 1 / -1) => eq.mp (Eq._oldrec (Eq.refl (1 / a ≤ 1 / -1)) one_div_neg_one_eq_neg_one) this)
    (one_div_le_one_div_of_neg_of_le h1 h2)

/-!
### Results about halving.

The equalities also hold in fields of characteristic `0`. -/

theorem add_halves {α : Type u_1} [linear_ordered_field α] (a : α) : a / bit0 1 + a / bit0 1 = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / bit0 1 + a / bit0 1 = a)) (div_add_div_same a a (bit0 1))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a + a) / bit0 1 = a)) (Eq.symm (two_mul a))))
      (eq.mpr (id (Eq._oldrec (Eq.refl (bit0 1 * a / bit0 1 = a)) (mul_div_cancel_left a two_ne_zero))) (Eq.refl a)))

theorem sub_self_div_two {α : Type u_1} [linear_ordered_field α] (a : α) : a - a / bit0 1 = a / bit0 1 := sorry

theorem div_two_sub_self {α : Type u_1} [linear_ordered_field α] (a : α) : a / bit0 1 - a = -(a / bit0 1) := sorry

theorem add_self_div_two {α : Type u_1} [linear_ordered_field α] (a : α) : (a + a) / bit0 1 = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + a) / bit0 1 = a)) (Eq.symm (mul_two a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * bit0 1 / bit0 1 = a)) (mul_div_cancel a two_ne_zero))) (Eq.refl a))

theorem half_pos {α : Type u_1} [linear_ordered_field α] {a : α} (h : 0 < a) : 0 < a / bit0 1 :=
  div_pos h zero_lt_two

theorem one_half_pos {α : Type u_1} [linear_ordered_field α] : 0 < 1 / bit0 1 :=
  half_pos zero_lt_one

theorem div_two_lt_of_pos {α : Type u_1} [linear_ordered_field α] {a : α} (h : 0 < a) : a / bit0 1 < a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / bit0 1 < a)) (propext (div_lt_iff zero_lt_two))))
    (lt_mul_of_one_lt_right h one_lt_two)

theorem half_lt_self {α : Type u_1} [linear_ordered_field α] {a : α} : 0 < a → a / bit0 1 < a :=
  div_two_lt_of_pos

theorem one_half_lt_one {α : Type u_1} [linear_ordered_field α] : 1 / bit0 1 < 1 :=
  half_lt_self zero_lt_one

theorem add_sub_div_two_lt {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (h : a < b) : a + (b - a) / bit0 1 < b := sorry

/-!
### Miscellaneous lemmas
-/

theorem mul_sub_mul_div_mul_neg_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (hc : c ≠ 0) (hd : d ≠ 0) : (a * d - b * c) / (c * d) < 0 ↔ a / c < b / d := sorry

theorem mul_sub_mul_div_mul_neg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (hc : c ≠ 0) (hd : d ≠ 0) : a / c < b / d → (a * d - b * c) / (c * d) < 0 :=
  iff.mpr (mul_sub_mul_div_mul_neg_iff hc hd)

theorem mul_sub_mul_div_mul_nonpos_iff {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (hc : c ≠ 0) (hd : d ≠ 0) : (a * d - b * c) / (c * d) ≤ 0 ↔ a / c ≤ b / d := sorry

theorem mul_sub_mul_div_mul_nonpos {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (hc : c ≠ 0) (hd : d ≠ 0) : a / c ≤ b / d → (a * d - b * c) / (c * d) ≤ 0 :=
  iff.mpr (mul_sub_mul_div_mul_nonpos_iff hc hd)

theorem mul_le_mul_of_mul_div_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} (h : a * (b / c) ≤ d) (hc : 0 < c) : b * a ≤ d * c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b * a ≤ d * c)) (mul_comm b a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * b ≤ d * c)) (Eq.symm (propext (div_le_iff hc)))))
      (eq.mp (Eq._oldrec (Eq.refl (a * (b / c) ≤ d)) (Eq.symm mul_div_assoc)) h))

theorem div_mul_le_div_mul_of_div_le_div {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} {c : α} {d : α} {e : α} (h : a / b ≤ c / d) (he : 0 ≤ e) : a / (b * e) ≤ c / (d * e) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / (b * e) ≤ c / (d * e))) (div_mul_eq_div_mul_one_div a b e)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a / b * (1 / e) ≤ c / (d * e))) (div_mul_eq_div_mul_one_div c d e)))
      (mul_le_mul_of_nonneg_right h (iff.mpr one_div_nonneg he)))

theorem exists_add_lt_and_pos_of_lt {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (h : b < a) : ∃ (c : α), b + c < a ∧ 0 < c :=
  Exists.intro ((a - b) / bit0 1) { left := add_sub_div_two_lt h, right := div_pos (sub_pos_of_lt h) zero_lt_two }

theorem le_of_forall_sub_le {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (h : ∀ (ε : α), ε > 0 → b - ε ≤ a) : b ≤ a := sorry

theorem monotone.div_const {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [preorder β] {f : β → α} (hf : monotone f) {c : α} (hc : 0 ≤ c) : monotone fun (x : β) => f x / c :=
  monotone.mul_const hf (iff.mpr inv_nonneg hc)

theorem strict_mono.div_const {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [preorder β] {f : β → α} (hf : strict_mono f) {c : α} (hc : 0 < c) : strict_mono fun (x : β) => f x / c :=
  strict_mono.mul_const hf (iff.mpr inv_pos hc)

protected instance linear_ordered_field.to_densely_ordered {α : Type u_1} [linear_ordered_field α] : densely_ordered α :=
  densely_ordered.mk
    fun (a₁ a₂ : α) (h : a₁ < a₂) =>
      Exists.intro ((a₁ + a₂) / bit0 1)
        { left :=
            trans_rel_right Less (Eq.symm (add_self_div_two a₁)) (div_lt_div_of_lt zero_lt_two (add_lt_add_left h a₁)),
          right := trans_rel_left Less (div_lt_div_of_lt zero_lt_two (add_lt_add_right h a₂)) (add_self_div_two a₂) }

theorem mul_self_inj_of_nonneg {α : Type u_1} [linear_ordered_field α] {a : α} {b : α} (a0 : 0 ≤ a) (b0 : 0 ≤ b) : a * a = b * b ↔ a = b := sorry

theorem min_div_div_right {α : Type u_1} [linear_ordered_field α] {c : α} (hc : 0 ≤ c) (a : α) (b : α) : min (a / c) (b / c) = min a b / c :=
  Eq.symm (monotone.map_min fun (x y : α) => div_le_div_of_le hc)

theorem max_div_div_right {α : Type u_1} [linear_ordered_field α] {c : α} (hc : 0 ≤ c) (a : α) (b : α) : max (a / c) (b / c) = max a b / c :=
  Eq.symm (monotone.map_max fun (x y : α) => div_le_div_of_le hc)

theorem min_div_div_right_of_nonpos {α : Type u_1} [linear_ordered_field α] {c : α} (hc : c ≤ 0) (a : α) (b : α) : min (a / c) (b / c) = max a b / c :=
  Eq.symm (monotone.map_max fun (x y : α) => div_le_div_of_nonpos_of_le hc)

theorem max_div_div_right_of_nonpos {α : Type u_1} [linear_ordered_field α] {c : α} (hc : c ≤ 0) (a : α) (b : α) : max (a / c) (b / c) = min a b / c :=
  Eq.symm (monotone.map_min fun (x y : α) => div_le_div_of_nonpos_of_le hc)

theorem abs_div {α : Type u_1} [linear_ordered_field α] (a : α) (b : α) : abs (a / b) = abs a / abs b :=
  monoid_with_zero_hom.map_div abs_hom a b

theorem abs_one_div {α : Type u_1} [linear_ordered_field α] (a : α) : abs (1 / a) = 1 / abs a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (abs (1 / a) = 1 / abs a)) (abs_div 1 a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (abs 1 / abs a = 1 / abs a)) abs_one)) (Eq.refl (1 / abs a)))

theorem abs_inv {α : Type u_1} [linear_ordered_field α] (a : α) : abs (a⁻¹) = (abs a⁻¹) :=
  monoid_with_zero_hom.map_inv' abs_hom a


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.set.function
import Mathlib.logic.function.iterate
import Mathlib.PostPort

universes u v

namespace Mathlib

/-!
# Fixed points of a self-map

In this file we define

* the predicate `is_fixed_pt f x := f x = x`;
* the set `fixed_points f` of fixed points of a self-map `f`.

We also prove some simple lemmas about `is_fixed_pt` and `∘`, `iterate`, and `semiconj`.

## Tags

fixed point
-/

namespace function

/-- A point `x` is a fixed point of `f : α → α` if `f x = x`. -/
def is_fixed_pt {α : Type u} (f : α → α) (x : α) := f x = x

/-- Every point is a fixed point of `id`. -/
theorem is_fixed_pt_id {α : Type u} (x : α) : is_fixed_pt id x := rfl

namespace is_fixed_pt

protected instance decidable {α : Type u} [h : DecidableEq α] {f : α → α} {x : α} :
    Decidable (is_fixed_pt f x) :=
  h (f x) x

/-- If `x` is a fixed point of `f`, then `f x = x`. This is useful, e.g., for `rw` or `simp`.-/
protected theorem eq {α : Type u} {f : α → α} {x : α} (hf : is_fixed_pt f x) : f x = x := hf

/-- If `x` is a fixed point of `f` and `g`, then it is a fixed point of `f ∘ g`. -/
protected theorem comp {α : Type u} {f : α → α} {g : α → α} {x : α} (hf : is_fixed_pt f x)
    (hg : is_fixed_pt g x) : is_fixed_pt (f ∘ g) x :=
  Eq.trans (congr_arg f hg) hf

/-- If `x` is a fixed point of `f`, then it is a fixed point of `f^[n]`. -/
protected theorem iterate {α : Type u} {f : α → α} {x : α} (hf : is_fixed_pt f x) (n : ℕ) :
    is_fixed_pt (nat.iterate f n) x :=
  iterate_fixed hf n

/-- If `x` is a fixed point of `f ∘ g` and `g`, then it is a fixed point of `f`. -/
theorem left_of_comp {α : Type u} {f : α → α} {g : α → α} {x : α} (hfg : is_fixed_pt (f ∘ g) x)
    (hg : is_fixed_pt g x) : is_fixed_pt f x :=
  Eq.trans (congr_arg f (Eq.symm hg)) hfg

/-- If `x` is a fixed point of `f` and `g` is a left inverse of `f`, then `x` is a fixed
point of `g`. -/
theorem to_left_inverse {α : Type u} {f : α → α} {g : α → α} {x : α} (hf : is_fixed_pt f x)
    (h : left_inverse g f) : is_fixed_pt g x :=
  Eq.trans (congr_arg g (Eq.symm hf)) (h x)

/-- If `g` (semi)conjugates `fa` to `fb`, then it sends fixed points of `fa` to fixed points
of `fb`. -/
protected theorem map {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {x : α}
    (hx : is_fixed_pt fa x) {g : α → β} (h : semiconj g fa fb) : is_fixed_pt fb (g x) :=
  Eq.trans (Eq.symm (semiconj.eq h x)) (congr_arg g hx)

end is_fixed_pt

/-- The set of fixed points of a map `f : α → α`. -/
def fixed_points {α : Type u} (f : α → α) : set α := set_of fun (x : α) => is_fixed_pt f x

protected instance fixed_points.decidable {α : Type u} [DecidableEq α] (f : α → α) (x : α) :
    Decidable (x ∈ fixed_points f) :=
  is_fixed_pt.decidable

@[simp] theorem mem_fixed_points {α : Type u} {f : α → α} {x : α} :
    x ∈ fixed_points f ↔ is_fixed_pt f x :=
  iff.rfl

/-- If `g` semiconjugates `fa` to `fb`, then it sends fixed points of `fa` to fixed points
of `fb`. -/
theorem semiconj.maps_to_fixed_pts {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {g : α → β}
    (h : semiconj g fa fb) : set.maps_to g (fixed_points fa) (fixed_points fb) :=
  fun (x : α) (hx : x ∈ fixed_points fa) => is_fixed_pt.map hx h

/-- Any two maps `f : α → β` and `g : β → α` are inverse of each other on the sets of fixed points
of `f ∘ g` and `g ∘ f`, respectively. -/
theorem inv_on_fixed_pts_comp {α : Type u} {β : Type v} (f : α → β) (g : β → α) :
    set.inv_on f g (fixed_points (f ∘ g)) (fixed_points (g ∘ f)) :=
  { left := fun (x : β) => id, right := fun (x : α) => id }

/-- Any map `f` sends fixed points of `g ∘ f` to fixed points of `f ∘ g`. -/
theorem maps_to_fixed_pts_comp {α : Type u} {β : Type v} (f : α → β) (g : β → α) :
    set.maps_to f (fixed_points (g ∘ f)) (fixed_points (f ∘ g)) :=
  fun (x : α) (hx : x ∈ fixed_points (g ∘ f)) => is_fixed_pt.map hx fun (x : α) => rfl

/-- Given two maps `f : α → β` and `g : β → α`, `g` is a bijective map between the fixed points
of `f ∘ g` and the fixed points of `g ∘ f`. The inverse map is `f`, see `inv_on_fixed_pts_comp`. -/
theorem bij_on_fixed_pts_comp {α : Type u} {β : Type v} (f : α → β) (g : β → α) :
    set.bij_on g (fixed_points (f ∘ g)) (fixed_points (g ∘ f)) :=
  set.inv_on.bij_on (inv_on_fixed_pts_comp f g) (maps_to_fixed_pts_comp g f)
    (maps_to_fixed_pts_comp f g)

/-- If self-maps `f` and `g` commute, then they are inverse of each other on the set of fixed points
of `f ∘ g`. This is a particular case of `function.inv_on_fixed_pts_comp`. -/
theorem commute.inv_on_fixed_pts_comp {α : Type u} {f : α → α} {g : α → α} (h : commute f g) :
    set.inv_on f g (fixed_points (f ∘ g)) (fixed_points (f ∘ g)) :=
  sorry

/-- If self-maps `f` and `g` commute, then `f` is bijective on the set of fixed points of `f ∘ g`.
This is a particular case of `function.bij_on_fixed_pts_comp`. -/
theorem commute.left_bij_on_fixed_pts_comp {α : Type u} {f : α → α} {g : α → α} (h : commute f g) :
    set.bij_on f (fixed_points (f ∘ g)) (fixed_points (f ∘ g)) :=
  sorry

/-- If self-maps `f` and `g` commute, then `g` is bijective on the set of fixed points of `f ∘ g`.
This is a particular case of `function.bij_on_fixed_pts_comp`. -/
theorem commute.right_bij_on_fixed_pts_comp {α : Type u} {f : α → α} {g : α → α} (h : commute f g) :
    set.bij_on g (fixed_points (f ∘ g)) (fixed_points (f ∘ g)) :=
  sorry

end Mathlib


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

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

/-!
# Univariate monomials

Preparatory lemmas for degree_basic.
-/

noncomputable section

namespace Polynomial

open Polynomial

universe u

variable {R : Type u} {a b : R} {m n : ℕ}

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

theorem monomial_one_eq_iff [Nontrivial R] {i j : ℕ} :
    (monomial i 1 : R[X]) = monomial j 1 ↔ i = j := by
  -- Porting note: `ofFinsupp.injEq` is required.
  simp_rw [← ofFinsupp_single, ofFinsupp.injEq]
  exact AddMonoidAlgebra.of_injective.eq_iff
#align polynomial.monomial_one_eq_iff Polynomial.monomial_one_eq_iff

instance infinite [Nontrivial R] : Infinite R[X] :=
  Infinite.of_injective (fun i => monomial i 1) fun m n h => by simpa [monomial_one_eq_iff] using h
#align polynomial.infinite Polynomial.infinite

theorem card_support_le_one_iff_monomial {f : R[X]} :
    Finset.card f.support ≤ 1 ↔ ∃ n a, f = monomial n a := by
  constructor
  · intro H
    rw [Finset.card_le_one_iff_subset_singleton] at H
    rcases H with ⟨n, hn⟩
    refine' ⟨n, f.coeff n, _⟩
    ext i
    by_cases hi : i = n
    · simp [hi, coeff_monomial]
    · have : f.coeff i = 0 := by
        rw [← not_mem_support_iff]
        exact fun hi' => hi (Finset.mem_singleton.1 (hn hi'))
      simp [this, Ne.symm hi, coeff_monomial]
  · rintro ⟨n, a, rfl⟩
    rw [← Finset.card_singleton n]
    apply Finset.card_le_of_subset
    exact support_monomial' _ _
#align polynomial.card_support_le_one_iff_monomial Polynomial.card_support_le_one_iff_monomial

theorem ringHom_ext {S} [Semiring S] {f g : R[X] →+* S} (h₁ : ∀ a, f (C a) = g (C a))
    (h₂ : f X = g X) : f = g := by
  set f' := f.comp (toFinsuppIso R).symm.toRingHom with hf'
  set g' := g.comp (toFinsuppIso R).symm.toRingHom with hg'
  have A : f' = g' := by
    -- Porting note: Was `ext; simp [..]; simpa [..] using h₂`.
    ext : 1
    · ext
      simp [h₁, RingEquiv.toRingHom_eq_coe]
    · refine MonoidHom.ext_mnat ?_
      simpa [RingEquiv.toRingHom_eq_coe] using h₂
  have B : f = f'.comp (toFinsuppIso R) := by
    rw [hf', RingHom.comp_assoc]
    ext x
    simp only [RingEquiv.toRingHom_eq_coe, RingEquiv.symm_apply_apply, Function.comp_apply,
      RingHom.coe_comp, RingEquiv.coe_toRingHom]
  have C' : g = g'.comp (toFinsuppIso R) := by
    rw [hg', RingHom.comp_assoc]
    ext x
    simp only [RingEquiv.toRingHom_eq_coe, RingEquiv.symm_apply_apply, Function.comp_apply,
      RingHom.coe_comp, RingEquiv.coe_toRingHom]
  rw [B, C', A]
#align polynomial.ring_hom_ext Polynomial.ringHom_ext

@[ext high]
theorem ringHom_ext' {S} [Semiring S] {f g : R[X] →+* S} (h₁ : f.comp C = g.comp C)
    (h₂ : f X = g X) : f = g :=
  ringHom_ext (RingHom.congr_fun h₁) h₂
#align polynomial.ring_hom_ext' Polynomial.ringHom_ext'

end Polynomial


/-
Copyright (c) 2020 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.abelian.pseudoelements
import Mathlib.PostPort

universes v u

namespace Mathlib

/-!
# The four lemma

Consider the following commutative diagram with exact rows in an abelian category:

A ---f--> B ---g--> C ---h--> D
|         |         |         |
α         β         γ         δ
|         |         |         |
v         v         v         v
A' --f'-> B' --g'-> C' --h'-> D'

We prove the "mono" version of the four lemma: if α is an epimorphism and β and δ are monomorphisms,
then γ is a monomorphism.

## Future work

The "epi" four lemma and the five lemma, which is then an easy corollary.

## Tags

four lemma, diagram lemma, diagram chase
-/

namespace category_theory.abelian

/-- The four lemma, mono version. For names of objects and morphisms, consider the following
    diagram:

```
A ---f--> B ---g--> C ---h--> D
|         |         |         |
α         β         γ         δ
|         |         |         |
v         v         v         v
A' --f'-> B' --g'-> C' --h'-> D'
```
-/
theorem mono_of_epi_of_mono_of_mono {V : Type u} [category V] [abelian V] {A : V} {B : V} {C : V} {D : V} {A' : V} {B' : V} {C' : V} {D' : V} {f : A ⟶ B} {g : B ⟶ C} {h : C ⟶ D} {f' : A' ⟶ B'} {g' : B' ⟶ C'} {h' : C' ⟶ D'} {α : A ⟶ A'} {β : B ⟶ B'} {γ : C ⟶ C'} {δ : D ⟶ D'} [exact f g] [exact g h] [exact f' g'] (comm₁ : α ≫ f' = f ≫ β) (comm₂ : β ≫ g' = g ≫ γ) (comm₃ : γ ≫ h' = h ≫ δ) (hα : epi α) (hβ : mono β) (hδ : mono δ) : mono γ := sorry


import measure_theory.integral.interval_integral
import analysis.special_functions.integrals
import analysis.convolution

open set filter
open_locale topological_space filter

noncomputable theory

/- TEXT:
.. index:: integration

.. _elementary_integration:

Elementary Integration
----------------------

We first focus on integration of functions on finite intervals in ``ℝ``. We can integrate
elementary functions.
EXAMPLES: -/
-- QUOTE:
open measure_theory interval_integral
open_locale interval  -- this introduces the notation [a, b]

example (a b : ℝ): ∫ x in a..b, x = (b ^ 2 - a ^ 2) / 2 :=
integral_id

example {a b : ℝ}  (h : (0:ℝ) ∉ [a, b]) : ∫ x in a..b, 1/x = real.log (b / a) :=
integral_one_div h
-- QUOTE.

/- TEXT:
The fundamental theorem of calculus relates integration and differentiation.
Below we give simplified statementa of the two parts of this theorem. The first part
says that integration provides an inverse to differentiation and the second one
specifies how to compute integrals of derivatives.
(These two parts are very closely related, but their optimal versions,
which are not shown here, are not equivalent.)
EXAMPLES: -/
-- QUOTE:
example (f : ℝ → ℝ) (hf : continuous f) (a b : ℝ) :
  deriv (λ u, ∫ (x : ℝ) in a..u, f x) b = f b :=
(integral_has_strict_deriv_at_right
    (hf.interval_integrable _ _) (hf.strongly_measurable_at_filter _ _)
  hf.continuous_at).has_deriv_at.deriv

example {f : ℝ → ℝ} {a b : ℝ} {f' : ℝ → ℝ}
  (h : ∀ x ∈ [a, b], has_deriv_at f (f' x) x) (h' : interval_integrable f' volume a b) :
  ∫ y in a..b, f' y = f b - f a :=
integral_eq_sub_of_has_deriv_at h h'
-- QUOTE.

/- TEXT:
Convolution is also defined in mathlib and its basic properties are proved.
EXAMPLES: -/

-- QUOTE:
open_locale convolution

example  (f : ℝ → ℝ) (g : ℝ → ℝ) :
  f ⋆ g = λ x, ∫ t, (f t) * (g (x - t)) :=
rfl
-- QUOTE.


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import algebra.big_operators.basic
import data.nat.prime
import data.zmod.basic
import ring_theory.multiplicity
import data.nat.periodic
import algebra.char_p.two

/-!
# 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_locale big_operators

namespace nat

/-- 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

localized "notation `φ` := nat.totient" in nat

@[simp] theorem totient_zero : φ 0 = 0 := rfl

@[simp] theorem totient_one : φ 1 = 1 :=
by simp [totient]

lemma totient_eq_card_coprime (n : ℕ) : φ n = ((range n).filter n.coprime).card := rfl

lemma totient_le (n : ℕ) : φ n ≤ n :=
calc totient n ≤ (range n).card : card_filter_le _ _
           ... = n              : card_range _

lemma totient_lt (n : ℕ) (hn : 1 < n) : φ n < n :=
calc totient n ≤ ((range n).filter (≠ 0)).card :
  begin
    apply card_le_of_subset (monotone_filter_right _ _),
    intros n1 hn1 hn1',
    simpa only [hn1', coprime_zero_right, hn.ne'] using hn1,
  end
... = n - 1
    : by simp only [filter_ne' (range n) 0, card_erase_of_mem, card_range, pos_of_gt hn, mem_range]
... < n : nat.sub_lt (pos_of_gt hn) zero_lt_one

lemma totient_pos : ∀ {n : ℕ}, 0 < n → 0 < φ n
| 0 := dec_trivial
| 1 := by simp [totient]
| (n+2) := λ h, card_pos.2 ⟨1, mem_filter.2 ⟨mem_range.2 dec_trivial, coprime_one_right _⟩⟩

lemma filter_coprime_Ico_eq_totient (a n : ℕ) :
  ((Ico n (n+a)).filter (coprime a)).card = totient a :=
begin
  rw [totient, filter_Ico_card_eq_of_periodic, count_eq_card_filter_range],
  exact periodic_coprime a,
end

lemma Ico_filter_coprime_le {a : ℕ} (k n : ℕ) (a_pos : 0 < a) :
  ((Ico k (k + n)).filter (coprime a)).card ≤ totient a * (n / a + 1) :=
begin
  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, 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 :
        begin
          congr,
          rw Ico_union_Ico_eq_Ico,
          rw add_assoc,
          exact le_self_add,
          exact le_self_add,
        end
  ... ≤ (filter a.coprime (Ico k (k + n % a + a * i))).card + a.totient :
        begin
          rw [filter_union, ←filter_coprime_Ico_eq_totient a (k + n % a + a * i)],
          apply card_union_le,
        end
  ... ≤ a.totient * i + a.totient + a.totient : add_le_add_right ih (totient a),
end

open zmod

/-- Note this takes an explicit `fintype ((zmod n)ˣ)` argument to avoid trouble with instance
diamonds. -/
@[simp] lemma _root_.zmod.card_units_eq_totient (n : ℕ) [fact (0 < 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.units_equiv_coprime
... = φ n :
begin
  apply finset.card_congr (λ (a : {x : zmod n // x.val.coprime n}) _, a.1.val),
  { intro a, simp [(a : zmod n).val_lt, a.prop.symm] {contextual := tt} },
  { intros _ _ _ _ h, rw subtype.ext_iff_val, apply val_injective, exact h, },
  { intros b hb,
    rw [finset.mem_filter, finset.mem_range] at hb,
    refine ⟨⟨b, _⟩, finset.mem_univ _, _⟩,
    { let u := unit_of_coprime b hb.2.symm,
      exact val_coe_unit_coprime u },
    { show zmod.val (b : zmod n) = b,
      rw [val_nat_cast, nat.mod_eq_of_lt hb.1], } }
end

lemma totient_even {n : ℕ} (hn : 2 < n) : even n.totient :=
begin
  haveI : fact (1 < n) := ⟨one_lt_two.trans hn⟩,
  suffices : 2 = order_of (-1 : (zmod n)ˣ),
  { rw [← zmod.card_units_eq_totient, even_iff_two_dvd, this], exact order_of_dvd_card_univ },
  rw [←order_of_units, units.coe_neg_one, order_of_neg_one, ring_char.eq (zmod n) n, if_neg hn.ne'],
end

lemma 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, mul_zero, zero_mul, h]
  else
  begin
    haveI : fact (0 < (m * n)) := ⟨nat.pos_of_ne_zero hmn0⟩,
    haveI : fact (0 < m) := ⟨nat.pos_of_ne_zero $ left_ne_zero_of_mul hmn0⟩,
    haveI : fact (0 < n) := ⟨nat.pos_of_ne_zero $ right_ne_zero_of_mul hmn0⟩,
    rw [← zmod.card_units_eq_totient, ← zmod.card_units_eq_totient,
      ← zmod.card_units_eq_totient, fintype.card_congr
      (units.map_equiv (zmod.chinese_remainder h).to_mul_equiv).to_equiv,
      fintype.card_congr (@mul_equiv.prod_units (zmod m) (zmod n) _ _).to_equiv,
      fintype.card_prod]
  end

/-- When `p` is prime, then the totient of `p ^ (n + 1)` is `p ^ n * (p - 1)` -/
lemma 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 begin
    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],
    intros a ha,
    split,
    { rintros hap b _ rfl,
      exact hap (dvd_mul_left _ _) },
    { rintros h ⟨b, rfl⟩,
      rw [pow_succ] at ha,
      exact h b (lt_of_mul_lt_mul_left ha (zero_le _)) (mul_comm _ _) }
  end
... = _ :
have h1 : set.inj_on (* p) (range (p ^ n)),
  from λ x _ y _, (nat.mul_left_inj hp.pos).1,
have h2 : (range (p ^ n)).image (* p) ⊆ range (p ^ (n + 1)),
  from λ a, begin
    simp only [mem_image, mem_range, exists_imp_distrib],
    rintros b h rfl,
    rw [pow_succ'],
    exact (mul_lt_mul_right hp.pos).2 h
  end,
begin
  rw [card_sdiff h2, card_image_of_inj_on h1, card_range,
    card_range, ← one_mul (p ^ n), pow_succ, ← tsub_mul,
    one_mul, mul_comm]
end

/-- When `p` is prime, then the totient of `p ^ n` is `p ^ (n - 1) * (p - 1)` -/
lemma 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 _

lemma totient_prime {p : ℕ} (hp : p.prime) : φ p = p - 1 :=
by rw [← pow_one p, totient_prime_pow hp]; simp

lemma totient_mul_of_prime_of_dvd {p n : ℕ} (hp : p.prime) (h : p ∣ n) :
  (p * n).totient = p * n.totient :=
begin
  by_cases hzero : n = 0,
  { simp [hzero] },
  { have hfin := (multiplicity.finite_nat_iff.2 ⟨hp.ne_one, zero_lt_iff.2 hzero⟩),
    have h0 : 0 < (multiplicity p n).get hfin := multiplicity.pos_of_dvd hfin h,
    obtain ⟨m, hm, hndiv⟩ := multiplicity.exists_eq_pow_mul_and_not_dvd hfin,
    rw [hm, ← mul_assoc, ← pow_succ, nat.totient_mul (coprime_comm.mp (hp.coprime_pow_of_not_dvd
      hndiv)), nat.totient_mul (coprime_comm.mp (hp.coprime_pow_of_not_dvd hndiv)), ← mul_assoc],
    congr,
    rw [ ← succ_pred_eq_of_pos h0, totient_prime_pow_succ hp, totient_prime_pow_succ hp,
      succ_pred_eq_of_pos h0, ← mul_assoc p, ← pow_succ, ← succ_pred_eq_of_pos h0, nat.pred_succ] }
end

lemma totient_eq_iff_prime {p : ℕ} (hp : 0 < p) : p.totient = p - 1 ↔ p.prime :=
begin
  refine ⟨λ 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 (λ n hn hnz, finset.filter_card_eq h n $ finset.mem_Ico.mpr ⟨_, hn⟩),
  rwa [succ_le_iff, pos_iff_ne_zero],
end

lemma card_units_zmod_lt_sub_one {p : ℕ} (hp : 1 < p) [fintype ((zmod p)ˣ)] :
  fintype.card ((zmod p)ˣ) ≤ p - 1 :=
begin
  haveI : fact (0 < p) := ⟨zero_lt_one.trans hp⟩,
  rw zmod.card_units_eq_totient p,
  exact nat.le_pred_of_lt (nat.totient_lt p hp),
end

lemma prime_iff_card_units (p : ℕ) [fintype ((zmod p)ˣ)] :
  p.prime ↔ fintype.card ((zmod p)ˣ) = p - 1 :=
begin
  by_cases hp : p = 0,
  { substI hp,
    simp only [zmod, not_prime_zero, false_iff, zero_tsub],
    -- the substI created an non-defeq but subsingleton instance diamond; resolve it
    suffices : fintype.card ℤˣ ≠ 0, { convert this },
    simp },
  haveI : fact (0 < p) := ⟨nat.pos_of_ne_zero hp⟩,
  rw [zmod.card_units_eq_totient, nat.totient_eq_iff_prime (fact.out (0 < p))],
end

@[simp] lemma totient_two : φ 2 = 1 :=
(totient_prime prime_two).trans (by norm_num)

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

We prove several different statements of this formula. -/

/-- Euler's product formula for the totient function. -/
theorem totient_eq_prod_factorization {n : ℕ} (hn : n ≠ 0) :
  φ n = n.factorization.prod (λ p k, p ^ (k - 1) * (p - 1)) :=
begin
  rw multiplicative_factorization φ @totient_mul totient_one hn,
  apply finsupp.prod_congr (λ p hp, _),
  have h := zero_lt_iff.mpr (finsupp.mem_support_iff.mp hp),
  rw [totient_prime_pow (prime_of_mem_factorization hp) h],
end

/-- Euler's product formula for the totient function. -/
theorem totient_mul_prod_factors (n : ℕ) :
  φ n * ∏ p in n.factors.to_finset, p = n * ∏ p in n.factors.to_finset, (p - 1) :=
begin
  by_cases hn : n = 0, { simp [hn] },
  rw totient_eq_prod_factorization hn,
  nth_rewrite 2 ←factorization_prod_pow_eq_self hn,
  simp only [←prod_factorization_eq_prod_factors, ←finsupp.prod_mul],
  refine finsupp.prod_congr (λ p hp, _),
  rw [finsupp.mem_support_iff, ← zero_lt_iff] at hp,
  rw [mul_comm, ←mul_assoc, ←pow_succ, nat.sub_add_cancel hp],
end

/-- Euler's product formula for the totient function. -/
theorem totient_eq_div_factors_mul (n : ℕ) :
  φ n = n / (∏ p in n.factors.to_finset, p) * (∏ p in n.factors.to_finset, (p - 1)) :=
begin
  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 (λ p, pos_of_mem_factorization),
end

/-- Euler's product formula for the totient function. -/
theorem totient_eq_mul_prod_factors (n : ℕ) :
  (φ n : ℚ) = n * ∏ p in n.factors.to_finset, (1 - p⁻¹) :=
begin
  by_cases hn : n = 0, { simp [hn] },
  have hn' : (n : ℚ) ≠ 0, { simp [hn] },
  have hpQ : ∏ p in n.factors.to_finset, (p : ℚ) ≠ 0,
  { rw [←cast_prod, cast_ne_zero, ←zero_lt_iff, ←prod_factorization_eq_prod_factors],
    exact prod_pos (λ 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_dvd_char_zero, mul_comm_div', mul_right_inj' hn', div_eq_iff hpQ, ←prod_mul_distrib],
  refine prod_congr rfl (λ 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],
end

end nat