import logic.function.basic
import tactic

noncomputable theory
open_locale classical

open set function classical

/-
The Cantor-Bernstein Theorem: For any sets `A, B`, if there exist injections
`f: A → B` and `g: B → A`, there exists a bijection `h: A → B`.
-/

variables {A B : Type}

/-
The proof of the theorem is based on the following informal reasoning: Look at
the directed bipartite graph with vertex sets `A` and `B` and edges defined by
`f` and `g`. Each connected component of this graph can be seen to fall into
one of four categories:

1) An infinite path starting at a vertex in `A`,
2) An infinite path starting at a vertex in `B`,
3) An infinite path extending in both directions,
4) A directed cycle.

In case 1, we can create a bijection on that component by mapping each vertex
`a ∈ A` to the next one in the path, `f(a)`.
In case 2, we can create a bijection by mapping each vertex `a ∈ A` to the
previous one in the path, `g⁻¹(a)`.
In case 3 and 4, we could go with either the scheme for case 1 or the scheme
for case 2 -- we'll arbitrarily go with the case 1 scheme.
Taken together, these define a bijection `A → B`.

In other words, we want to define a function `h : A → B` such that
`h(a) = g⁻¹(a)` whenever `a` belongs to a case 2 component, and `h(a) = f(a)`
otherwise, and then argue that `h` is a bijection.

In order to define `h`, we should first define a predicate that tells whether
`a` belongs to a case 2 component. We start by defining a relation `follows`,
which, given vertices `a ∈ A` and `b ∈ B`, says whether `a` follows `b`, i.e.,
there's a directed path in the graph from `b` to `a`. (In retrospect, my
terminology may be a bit counterintuitive, depending on how you look at it.)

We can define this inductively. Our base case is when `a` comes immediately
after `b`, i.e., when `g(b) = a`. Then, if `a` follows `b`, then `g(f(a))`
will follow `b` as well.
-/

inductive follows (f : A → B) (g : B → A) : A → B → Prop
| base {a : A} {b : B} : g b = a → follows a b
| step {a : A} {b : B} : follows a b → follows (g (f a)) b

/-
Now we can define the set of elements of `A` in case 2 components; we call them
the `B_led` elements. Namely, these are the elements `a ∈ A` that follow some
`b ∈ B` which "leads" a path, i.e., there's nothing that maps to `b`.
-/

def B_led (f : A → B) (g : B → A) : set A :=
{ a : A | ∃ b : B, (follows f g a b ∧ ∀ a : A, f a ≠ b) }

/-
There's one more step before we can properly define `h`: we have to check that
the inverse of `g` is well-defined on `B_led` elements. This is "obvious",
since if nothing mapped to `a ∈ A`, then it would be the leader in an `A_led`
component. However, we still have to prove it!
-/

lemma has_g_inv {f : A → B} {g : B → A} {a : A} (hyp_a : B_led f g a) :
∃ b, g b = a :=
begin
  cases hyp_a with p hyp_p,
  replace hyp_p := hyp_p.left,
  induction hyp_p with a b,
  use b, assumption, use f hyp_p_a,
end

/-
Now, we can define a function that, given a proof that `a` is `B_led`, returns
`g⁻¹(a)`. (Well, technically, we're just returning *some* inverse of `a` -- we
haven't proved that the inverse is unique. We could do so easily, but it turns
out we don't need to.)
-/

def g_inv {f : A → B} {g : B → A} {a : A} (hyp_a : B_led f g a) : B :=
  some (has_g_inv hyp_a)

/-
And a quick lemma: `g(g⁻¹(a)) = a` for any `B_led` element `a`.
-/

lemma inv_eq {f : A → B} {g : B → A} {a : A} (hyp : B_led f g a) :
g (g_inv hyp) = a := some_spec (has_g_inv hyp)

/-
We're finally ready to prove the bulk of the theorem. We'll actually prove a
stronger version, which we used in our proof of the countable Hall matching
theorem: there exists a bijection that, regarded as a graph, is a subgraph of
the graph defined by `f` and `g`. In other words, whenever `h(a) = b`, we have
either `f(a) = b` or `g(b) = a`. This doesn't add much extra work, since it's
fairly obvious from the definition of `h`.
-/

theorem cantor_bernstein_strong {f : A → B} {g : B → A} (hyp_f : injective f)
(hyp_g : injective g) :
∃ h : A → B, bijective h ∧ ∀ a b, h a = b → f a = b ∨ g b = a :=
begin
  /-
  Here we define h: if `a` is `B_led`, then `h(a) = g⁻¹(a)`, else `h(a) = f(a)`.
  -/
  let h := λ a, dite (B_led f g a) (λ hyp_a, g_inv hyp_a) (λ _, f a),
  use h,
  /-
  We prove injectivity, then surjectivity, then the added "strong" property.
  -/
  split, split,
  {
    /-
    The bulk of the proof of injectivity is showing that, if `h(a₁) = h(a₂)`,
    then `a₁` and `a₂` belong to the same connected component. Thus, they're
    either both `B_led` or both not, and in either case injectivity follows
    pretty much immediately.

    The proof can be informally stated as follows:

    Suppose for sake of contradiction that `h(a₁) = h(a₂)`, but `a₁` is `B_led`
    and `a₂` isn't. Then `g⁻¹(a₁) = f(a₂)`, so `a₁ = g(f(a₂))`.

    Intuitively, we can see that this means `a₁` comes 2 steps after `a₂` in a
    path, so they're in the same connected component, a contradiction.

    Putting it formally requires us to go back to our inductive definition of
    "following". Let `b` be the leader of `a₁`'s component, so `a` follows `b`.

    The `base` case where `a₁` immediately follows `b` is impossible, since
    otherwise `a₂` would point to `b`, contradicting `b`'s status as leader.

    But this leaves us with the `step` case, which implies there's some `a`
    for which `a` follows `b` and `f(g(a)) = a₁ = f(g(a₂))`. Injectivity then
    implies `a = a₂`, so `a₂` follows `b`, and is thus `B_led` -- contradiction.
    -/
    have impossible_case : ∀ a₁ a₂ : A,
    h a₁ = h a₂ → B_led f g a₁ → ¬B_led f g a₂ → false :=
    begin
      intros a₁ a₂ hyp_a hyp_1 hyp_2,
      apply hyp_2,
      simp [h, dif_pos hyp_1, dif_neg hyp_2] at hyp_a,
      clear hyp_2 h,
      replace hyp_a := congr_arg g hyp_a, rw inv_eq hyp_1 at hyp_a,

      cases hyp_1 with b hyp_b,
      use b,
      cases hyp_b,
      rw and_iff_left hyp_b_right,
      cases hyp_b_left with _ _ hyp_base a _ hyp_step,
      {
        apply false.elim, apply hyp_b_right a₂,
        apply hyp_g,
        rw [← hyp_a, hyp_base],
      },
      {
        rwa ← hyp_f (hyp_g hyp_a),
      },
    end,
    /-
    This finishes the "impossible case" -- the rest of the injectivity proof is
    pretty straightforward.
    -/
    intros a₁ a₂ hyp_a,
    cases classical.em (B_led f g a₁) with hyp_1 hyp_1;
    cases classical.em (B_led f g a₂) with hyp_2 hyp_2,
    {
      simp [h, dif_pos hyp_1, dif_pos hyp_2] at hyp_a,
      rw [← inv_eq hyp_1, ← inv_eq hyp_2],
      exact congr_arg g hyp_a,
    },
    {
      exact false.elim (impossible_case a₁ a₂ hyp_a hyp_1 hyp_2),
    },
    {
      exact false.elim (impossible_case a₂ a₁ (hyp_a.symm) hyp_2 hyp_1),
    },
    {
      simp [h, dif_neg hyp_1, dif_neg hyp_2] at hyp_a,
      exact hyp_f hyp_a,
    },
  },
  {
    /-
    The proof of surjectivity is roughly as follows:

    Take an arbitrary `b ∈ B`, and do casework on whether `g(b)` is `B_led`.

    If `g(b)` is `B_led`, then `h(g(b)) = g⁻¹(g(b)) = b`, so `g(b)` works and
    we're done.

    Otherwise, `g(b)` isn't `B_led`. This implies that there exists `a` such
    that `f(a) = b` -- otherwise, `b` would be a leader of `g(b)`'s component.

    Note that `a` can't be `B_led`, since otherwise `g(b) = g(f(a))` would also
    be `B_led` (as immediately implied by the `step` case).

    Thus, `h(a) = f(a) = b`, so `a` works and we're done.
    -/
    intro b,
    cases classical.em (B_led f g (g b)) with hyp_1 hyp_1,
    {
      use g b,
      simp [h, dif_pos hyp_1],
      apply hyp_g, apply inv_eq,
    },
    {
      have has_f_inv : ∃ a, f a = b :=
      begin
        rw [B_led, ← @mem_def _ _ {a | _}, nmem_set_of_eq] at hyp_1,
        simp at hyp_1,
        specialize hyp_1 b,
        apply hyp_1,
        exact follows.base rfl,
      end,
      cases has_f_inv with a hyp_2,
      use a,
      rw ← hyp_2 at hyp_1,
      replace hyp_1 : ¬B_led f g a :=
      begin
        revert hyp_1, rw not_imp_not, intros hyp,
        cases hyp with b hyp_b,
        use b,
        exact ⟨(follows.step hyp_b.left), hyp_b.right⟩,
      end,
      simpa [h, dif_neg hyp_1],
    },
  },
  {
    /-
    All that's left is to prove the extra fact about `h`, but this is very
    straightforward.
    -/
    intros a b hyp,
    simp [h] at hyp,
    cases classical.em (B_led f g a) with hyp_pos hyp_neg,
    {
      replace hyp := congr_arg g hyp,
      rw [dif_pos hyp_pos, inv_eq hyp_pos] at hyp,
      exact or.intro_right _ hyp.symm,
    },
    {
      rw dif_neg hyp_neg at hyp,
      exact or.intro_left _ hyp,
    },
  },
end

/-
We've now formally proved the Cantor-Bernstein theorem -- yay!

Here's the "normal" version, which obviously follows from the "strong" version.
-/

theorem cantor_bernstein {f : A → B} {g : B → A} (hyp_f : injective f)
(hyp_g : injective g) : ∃ h : A → B, bijective h :=
begin
  have cbs := cantor_bernstein_strong hyp_f hyp_g,
  cases cbs with h hyp_h,
  exact Exists.intro h hyp_h.left,
end


/-
Copyright (c) 2021 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import logic.basic

/-!
# Girard's paradox

Girard's paradox is a proof that `Type : Type` entails a contradiction. We can't say this directly
in Lean because `Type : Type 1` and it's not possible to give `Type` a different type via an axiom,
so instead we axiomatize the behavior of the Pi type and application if the typing rule for Pi was
`(Type → Type) → Type` instead of `(Type → Type) → Type 1`.

Furthermore, we don't actually want false axioms in mathlib, so rather than introducing the axioms
using `axiom` or `constant` declarations, we take them as assumptions to the `girard` theorem.

Based on Watkins' LF implementation of Hurkens' simplification of Girard's paradox:
<http://www.cs.cmu.edu/~kw/research/hurkens95tlca.elf>.

## Main statements

* `girard`: there are no Girard universes.
-/

/-- **Girard's paradox**: there are no universes `u` such that `Type u : Type u`.
Since we can't actually change the type of Lean's `Π` operator, we assume the existence of
`pi`, `lam`, `app` and the `beta` rule equivalent to the `Π` and `app` constructors of type theory.
-/
theorem {u} girard
  (pi : (Type u → Type u) → Type u)
  (lam : ∀ {A : Type u → Type u}, (∀ x, A x) → pi A)
  (app : ∀ {A}, pi A → ∀ x, A x)
  (beta : ∀ {A : Type u → Type u} (f : ∀ x, A x) (x), app (lam f) x = f x) : false :=
let F (X) := (set (set X) → X) → set (set X), U := pi F in
let G (T : set (set U)) (X) : F X := λ f, {p | {x : U | f (app x X f) ∈ p} ∈ T} in
let τ (T : set (set U)) : U := lam (G T) in
let σ (S : U) : set (set U) := app S U τ in
have στ : ∀ {s S}, s ∈ σ (τ S) ↔ {x | τ (σ x) ∈ s} ∈ S := λ s S,
  iff_of_eq (congr_arg (λ f : F U, s ∈ f τ) (beta (G S) U) : _),
let ω : set (set U) := {p | ∀ x, p ∈ σ x → x ∈ p} in
let δ (S : set (set U)) := ∀ p, p ∈ S → τ S ∈ p in
have δ ω := λ p d, d (τ ω) $ στ.2 $ λ x h, d (τ (σ x)) (στ.2 h),
this {y | ¬ δ (σ y)} (λ x e f, f _ e (λ p h, f _ (στ.1 h))) (λ p h, this _ (στ.1 h))


import data.int.basic
import data.nat.parity
import data.nat.prime
import tactic
import tactic.linarith

namespace lecture4

open nat

theorem div_14_div_7 : ∀ N, 14 ∣ N → 7 ∣ N :=
begin
  intros N h,
  cases h with divisor h,
  refine dvd.intro _ _,
  let divisor' := 2 * divisor,
  use divisor',
  simp,
  linarith,
end

theorem prime_and_prime_succ : ∀ p, prime p → prime (p + 1) → p = 2 :=
begin
  intros p hprime hprimesucc,
  cases even_or_odd p,
  {
    cases h with n hn,
    let hprime2 := hprime,
    cases hprime,
    specialize hprime_right n,
    have n_div_p : n ∣ p,
    {
      exact dvd.intro_left 2 (eq.symm hn)
    },

    cases hprime_right n_div_p,
    {
      linarith,
    },

    have p_zero : p = 0,
    {
      rw h at hn,
      linarith,
    },

    by_contradiction h',
    apply not_prime_zero,
    rw p_zero at hprime2,
    exact hprime2,
  },

  let h2 := h,
  cases h with n hn,
  cases hprimesucc,

  have n_div_succ_p : 2 ∣ succ p,
  {
    refine even_iff_two_dvd.mp _,
    refine even_succ.mpr _,
    exact odd_iff_not_even.mp h2,
  },

  specialize hprimesucc_right 2,
  cases hprimesucc_right n_div_succ_p,
  {
    by_contradiction h',
    linarith,
  },

  have n_zero : n = 0,
  {
    linarith,
  },

  rw n_zero at hn,
  by_contradiction,
  refine not_prime_one _,
  have p_one : p = 1,
  {
    linarith,
  },
  rw ←p_one,
  exact hprime,
end

lemma only_even_prime_is_2 : ∀ {n}, prime n -> even n -> n = 2 :=
begin
  intros n hp he,
  cases hp,
  cases he,
  specialize hp_right 2,
  cases hp_right (dvd.intro he_w (eq.symm he_h) : 2 ∣ n),
  {
    finish,
  },
  linarith,
end

theorem prime_and_prime_succ2
      {p} (hprime : prime p) (hprimesucc : prime (p + 1)) : p = 2 :=
begin
  cases even_or_odd p,
  {
    exact only_even_prime_is_2 hprime h,
  },
  exfalso,
  have p_succ_even : even (p + 1) := begin
    refine even_succ.mpr _,
    exact odd_iff_not_even.mp h,
  end,
  have p_succ_is_two : (p + 1) = 2 := only_even_prime_is_2 hprimesucc p_succ_even,
  have p_is_one : p = 1 := by linarith,
  refine not_prime_one _,
  rw p_is_one at hprime,
  exact hprime,
end

-- this should be defined over ℤ
theorem bounds_by_divisibility
    {a b : ℕ} (a_divs_b : a ∣ b) (b_neq_0 : b ≠ 0)
    : a ≤ b :=
begin
  refine le_of_dvd _ a_divs_b,
  by_contradiction,
  have b_leq_0 : b ≤ 0 := by linarith,
  finish,
end

theorem div_trans
    {a b c : ℕ} (a_divs_b : a ∣ b) (b_divs_c : b ∣ c)
    : a ∣ c :=
begin
  cases a_divs_b with k hk,
  cases b_divs_c with i hi,
  refine dvd.intro _ _,
  use k * i,
  finish,
end

theorem div_of_int_combo
    {a b c : ℕ} (a_divs_b : a ∣ b) (b_divs_c : b ∣ c)
    : ∀ x y, a ∣ (b * x + c * y) :=
begin
  intros x y,
  cases a_divs_b with k hk,
  cases b_divs_c with i hi,
  refine dvd.intro _ _,
  use (k*x + k*i*y),
  rw hi,
  repeat {rw hk},
  linarith,
end

end lecture4


import data.real.basic
import data.pnat.basic
import solutions_sheet_two

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

def is_limit (a : ℕ+ → ℝ) (l : ℝ) : Prop :=
∀ ε > 0, ∃ N, ∀ n ≥ N, | a n - l | < ε

def is_convergent (a : ℕ+ → ℝ) : Prop :=
∃ l : ℝ, is_limit a l

def is_not_convergent (a : ℕ+ → ℝ) : Prop := ¬ is_convergent a

/-!

# Q1

-/

/-

Which of the following sequences are convergent and which are not?
What's the limit of the convergent ones?

-/

theorem Q1a : is_limit (λ n, (n+7)/n) 1 :=
begin
  intros ε hε,
  use (ceil ((37 : ℝ) / ε)).nat_abs,
  { apply int.nat_abs_pos_of_ne_zero,
    apply norm_num.ne_zero_of_pos,
    rw ceil_pos,
    refine div_pos _ hε,
    norm_num },
  rintros ⟨n, hn0⟩ hn,
  simp only [pnat.mk_coe, coe_coe],
  rw add_div,
  rw div_self (show (n : ℝ) ≠ 0, by norm_cast; linarith),
  simp only [add_sub_cancel'],
  rw abs_lt,
  split,
  { refine lt_trans (show -ε < 0, by linarith) _,
    refine div_pos (by norm_num) _,
    assumption_mod_cast },
  { rw div_lt_iff, swap, assumption_mod_cast,
    simp at hn,

    replace hn := le_trans int.le_nat_abs (int.coe_nat_le.mpr hn),
    rw ceil_le at hn,
    norm_cast at hn,
    rw div_le_iff hε at hn,
    linarith }
end

theorem Q1b : is_limit (λ n, n/(n+7)) 1 :=
begin
  sorry
end

theorem Q1c : is_limit (λ n, (n^2+5*n+6)/(n^3-2)) 0 :=
begin
  sorry
end

theorem Q1d : is_not_convergent (λ n, (n^3-2)/(n^2+5*n+6)) :=
begin
  sorry
end

def is_cauchy (a : ℕ+ → ℝ) : Prop :=
∀ ε > 0, ∃ N : ℕ+, ∀ m n ≥ N, |a m - a n| < ε

theorem is_cauchy_of_is_convergent {a : ℕ+ → ℝ} : is_convergent a → is_cauchy a :=
begin
  rintro ⟨l, hl⟩,
  intros ε hε,
  specialize hl (ε/2) (by linarith),
  rcases hl with ⟨B, hB1⟩,
  use B,
  intros m n hmB hnB,
  have hm := hB1 m hmB,
  have hn := hB1 n hnB,
  have h := abs_add (a m - l) (l - a n),
  have h2 : a m - l + (l - a n) = a m - a n := by ring,
  rw h2 at h,
  rw abs_sub l at h,
  linarith,
end

theorem Q1e : is_not_convergent (λ n, (1 - n*(-1)^n.1)/n) :=
begin
  intro h,
  replace h := is_cauchy_of_is_convergent h,
  -- 1/n + (-1)^{n+1}
  specialize h 0.1 (by linarith),
  cases h with N hN,
  have htemp : N ≤ N + 1,
    cases N with N hN,
    change N ≤ _,
    simp,
  specialize hN N (N + 1) (le_refl N) htemp,
  rw abs_lt at hN,
  dsimp only at hN,
  cases hN with hN1 hN2,
  cases N with N hN,
  simp at *,
  sorry
end

/-!

# Q3

-/

/-

Let a_n be a sequence converging to a ∈ ℝ. Suppose b_n is another
sequence which is different than a_n but only differs from a_n
in finitely many terms, that is the set {n : ℕ | a_n ≠ b_n} is
non-empty and finite. Prove b_n converges to a
-/

theorem Q3 (a : ℕ+ → ℝ) (b : ℕ+ → ℝ) (h_never_used : {n : ℕ+ | a n ≠ b n}.nonempty)
  (h : {n : ℕ+ | a n ≠ b n}.finite) (l : ℝ) (ha : is_limit a l) : is_limit b l :=
begin
  intros ε hε,
  cases ha ε hε with B hB,
  let S : finset ℕ+ := h.to_finset,
  have hS : S.nonempty,
    simp only [h_never_used, set.finite.to_finset.nonempty],
  let m := finset.max' S hS,
  use max B (m + 1),
  intros n hn,
  convert hB n _,
  { suffices : n ∉ {n | a n ≠ b n},
      symmetry,
      by_contra htemp,
      apply this,
      exact htemp,
    intro htemp,
    have hS : n ∈ S,
      simp [htemp],
      exact htemp,
    have ZZZ : n ≤ m := finset.le_max' S n hS,
    change max _ _ ≤ n at hn,
    rw max_le_iff at hn,
    cases hn,
    have YYY := le_trans hn_right ZZZ,
    change m.1 + 1 ≤ m.1 at YYY,
    linarith },
  change _ ≤ _ at hn,
  rw max_le_iff at hn,
  exact hn.1,
end

/-!

# Q4

-/

/-

Let S ⊆ ℝ be a nonempty bounded above set. show that there exists
a sequence of numbers s_n ∈ S, n = 1, 2, 3,… such that sₙ → Sup S

-/

--theorem useful_lemma {S : set ℝ} {a : ℝ} (haS : is_lub S a) (t : ℝ)
--  (ht : t < a) : ∃ s, s ∈ S ∧ t < s :=

theorem useful_lemma2 (S : set ℝ) (hS1 : S.nonempty) (hS2 : bdd_above S) :
  is_lub S (Sup S) :=
begin
  cases hS1 with a ha,
  cases hS2 with b hb,
  apply real.is_lub_Sup ha hb,
end

theorem pnat_aux_lemma {ε : ℝ} (hε : 0 < ε) {a : ℝ} (ha : 0 < a) : 0 < ⌈a / ε⌉.nat_abs :=
begin
  apply int.nat_abs_pos_of_ne_zero,
  apply norm_num.ne_zero_of_pos,
  rw ceil_pos,
  refine div_pos _ hε,
  exact ha,
end

theorem Q4 (S : set ℝ) (hS1 : S.nonempty) (hS2 : bdd_above S) :
  ∃ s : ℕ+ → ℝ, (∀ n, s n ∈ S) ∧ is_limit s (Sup S) :=
begin
  have h := useful_lemma (useful_lemma2 S hS1 hS2),
--  by_contra h2,
--  unfold is_limit at h2,
--  push_neg at h2,
  let s : ℕ+ → ℝ :=
  λ n, classical.some (h (Sup S - 1/n) (show Sup S - 1/n < Sup S, from _)),
  have hs : ∀ n : ℕ+, _ :=
  λ n, classical.some_spec (h (Sup S - 1/n) (show Sup S - 1/n < Sup S, from _)),
  use s,
  swap,
  { cases n with n hn,
    rw sub_lt_self_iff,
    rw one_div_pos,
    simp [hn] },
  have hs2 : ∀ (n : ℕ+), s n ∈ S,
  { intro n,
    dsimp at hs,
    exact (hs n).1 },
  use hs2,
  intros ε hε,
  use (ceil (2/ε)).nat_abs,
    exact pnat_aux_lemma hε (show (0 : ℝ) < 2, by norm_num),
  intros n hn,
  have hn2 : Sup S - 1/n < s n,
    exact (hs n).2,
  rw sub_lt at hn2,
  have hS3 := useful_lemma2 S hS1 hS2,
  rw abs_lt,
  split,
  { suffices : (1 : ℝ) / n < ε,
      linarith,
    cases n with n hn3, --
    show (1 : ℝ) / n < _,
    rw div_lt_iff (show 0 < (n : ℝ), by assumption_mod_cast),
    simp at hn,
    replace hn := le_trans (int.le_nat_abs) (int.coe_nat_le.mpr hn),
    rw ceil_le at hn,
    norm_cast at hn,
    rw div_le_iff at hn;
    linarith },
  { refine lt_of_le_of_lt _ hε,
    specialize hs2 n,
    rw sub_nonpos,
    cases hS3 with hS4 hS5,
    apply hS4 hs2 },
end


import tactic
import data.real.sqrt
import data.int.modeq
import data.real.irrational
import data.nat.factorization.prime_pow

/-

For each of the following functions `f`, say whether `f` is `1-1` and whether `f`
is `onto`.

-/

def f1 (x : ℝ) : ℝ := x^2 + 2*x

noncomputable def f2 (x : ℝ) : ℝ :=
  if 1 < x then x - 2
  else if x < -1 then x + 2
  else -x

noncomputable def f3 (x : ℚ) : ℝ := (x + real.sqrt 2)^2

def f4 (mnr : ℕ × ℕ × ℕ) : ℕ :=
let ⟨m, n, r⟩ := mnr in
2 ^ m * 3 ^ n * 5 ^ r

def f5 (mnr : ℕ × ℕ × ℕ) : ℕ :=
let ⟨m, n, r⟩ := mnr in
2 ^ m * 3 ^ n * 6 ^ r

-- For the last question let's first make the equivalence relation
def e (a b : ℤ) : Prop := a ≡ b [ZMOD 7]

lemma he : equivalence e :=
⟨
  -- reflexive
  begin
    intro x,
    unfold e,
  end,
  -- symmetric
  begin
    intros x y h,
    unfold e at *,
    exact int.modeq.symm h,
  end,
  -- transitive
  begin
    intros x y z hxy hyz,
    unfold e at *,
    exact int.modeq.trans hxy hyz,
  end ⟩

-- Let's now say that `e` is the "canonical" equivalence relation on ℤ
instance s : setoid ℤ := ⟨e, he⟩

lemma s_def (a b : ℤ) : a ≈ b ↔ a ≡ b [ZMOD 7]:= iff.rfl

-- and now we can use the theory of quotients. The set `S` in the question
-- is called `quotient s` here.

def f6 (x : quotient s) : quotient s :=
quotient.map (λ t : ℤ, t + 1) begin
  -- Lean points out that if we don't show the below, then `f6` isn't well-defined!
  show ∀ a b : ℤ, a ≈ b → a + 1 ≈ b + 1,
  -- So we have to prove it now.
  intros a b hab,
  rw s_def at *,
  exact int.modeq.add_right 1 hab,
end x

-- `injective` is actually called `function.injective` so let's open `function`
open function

-- now we can just call it `injective`

/-

## The rules

If the functions are injective/surjective, prove the lemmas. If they're not,
then put `¬` in front of them (e.g. `exercise01inj : ¬ (injective f1)` and prove
that instead!

-/
lemma exercise01inj : ¬ (injective f1) :=
begin
  intro h,
  have hp : f1 (-2) = f1 0,
  {unfold f1, norm_num},
  specialize h hp,
  norm_num at h,
end

lemma exercise01surj : ¬ (surjective f1) :=
begin
  intro h,
  specialize h (-2),
  cases h with x hx,
  unfold f1 at hx,
  have hp : ∀ x : ℝ, x ^ 2 + 2 * x = -2 ↔ (x + 1)^2 = -1,
  {intro x, split, {intro h1, linear_combination h1},
  {intros h2, linear_combination h2},
  },
  specialize hp x,
  rw hp at hx,
  nlinarith,
end

lemma exercise02inj : ¬ (injective f2) :=
begin
  intro h,
  have hp : f2 (-1/2) = f2 (5/2),
  {unfold f2, split_ifs; linarith},
  specialize h hp,
  norm_num at h,
end

lemma exercise02surj : (surjective f2) :=
begin
  intro y,
  rcases lt_trichotomy y 0 with h1 | rfl | h3,
  {use (y-2), unfold f2, split_ifs; linarith},
  {use 0, unfold f2, split_ifs; linarith},
  {use (y+2), unfold f2, split_ifs; linarith},
end

lemma exercise03inj : injective f3 :=
begin
  intros a b hab,
  unfold f3 at hab,
  simp [mul_self_eq_mul_self_iff, pow_two] at hab,
  rcases hab with h1 | h2,
  {assumption},
  {exfalso, suffices h : real.sqrt 2 = -(a + b) / 2,
  {norm_cast at h, apply irrational_sqrt_two, use (-(a + b) / 2), exact h.symm,},
  {linear_combination h2 / 2}},
end

lemma exercise03surj : ¬ (surjective f3) :=
begin
  intro h,
  specialize h (-1),
  cases h with x h,
  unfold f3 at h,
  nlinarith,
end

lemma padic_val_nat_two_aux (a b c : ℕ) : padic_val_nat 2 (2 ^ a * 3 ^ b * 5 ^ c) = a :=
begin
  haveI : fact (nat.prime 2) := fact.mk nat.prime_two,
  rw [padic_val_nat.mul 2 (mul_ne_zero _ _), padic_val_nat.mul, padic_val_nat.prime_pow,
    padic_val_nat.eq_zero_of_not_dvd, padic_val_nat.eq_zero_of_not_dvd],
  { simp },
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_two h,
    norm_num at h, },
    { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_two h,
    norm_num at h, },
  all_goals {exact pow_ne_zero _ (by norm_num)},
end

lemma padic_val_nat_three_aux (a b c : ℕ) : padic_val_nat 3 (2 ^ a * 3 ^ b * 5 ^ c) = b :=
begin
  haveI : fact (nat.prime 3) := fact.mk nat.prime_three,
  rw [padic_val_nat.mul 3 (mul_ne_zero _ _), padic_val_nat.mul, padic_val_nat.prime_pow,
    padic_val_nat.eq_zero_of_not_dvd, padic_val_nat.eq_zero_of_not_dvd],
  { simp },
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_three h,
    norm_num at h, },
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_three h,
    norm_num at h, },
  all_goals {exact pow_ne_zero _ (by norm_num)},
end

lemma nat.prime_five : nat.prime 5 := by norm_num

lemma padic_val_nat_five_aux (a b c : ℕ) : padic_val_nat 5 (2 ^ a * 3 ^ b * 5 ^ c) = c :=
begin
  haveI : fact (nat.prime 5) := fact.mk nat.prime_five,
  rw [padic_val_nat.mul 5 (mul_ne_zero _ _), padic_val_nat.mul, padic_val_nat.prime_pow,
    padic_val_nat.eq_zero_of_not_dvd, padic_val_nat.eq_zero_of_not_dvd],
  { simp },
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_five h,
    norm_num at h, },
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_five h,
    norm_num at h, },
  all_goals {exact pow_ne_zero _ (by norm_num)},
end

lemma exercise04inj : injective f4 :=
begin
  rintro ⟨a1, b1, c1⟩ ⟨a2, b2, c3⟩ h,
  unfold f4 at h,
  simp,
  refine ⟨_, _, _⟩,
  { rw [← padic_val_nat_two_aux a1 b1 c1, h, padic_val_nat_two_aux], },
  { rw [← padic_val_nat_three_aux a1 b1 c1, h, padic_val_nat_three_aux], },
  { rw [← padic_val_nat_five_aux a1 b1 c1, h, padic_val_nat_five_aux], },
end

lemma nat.prime_seven : nat.prime 7 := by norm_num

lemma padic_val_nat_seven_aux (a b c : ℕ) : padic_val_nat 7 (2 ^ a * 3 ^ b * 5 ^ c) = 0 :=
begin
  haveI : fact (nat.prime 7) := fact.mk nat.prime_seven,
  rw [padic_val_nat.mul 7 (mul_ne_zero _ _), padic_val_nat.mul, padic_val_nat.eq_zero_of_not_dvd,
    padic_val_nat.eq_zero_of_not_dvd, padic_val_nat.eq_zero_of_not_dvd],
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_seven h,
    norm_num at h, },
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_seven h,
    norm_num at h, },
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_seven h,
    norm_num at h, },
  all_goals {exact pow_ne_zero _ (by norm_num)},
end

lemma exercise04surj : ¬ (surjective f4) :=
begin
  intro h,
  specialize h 7,
  cases h with x h,
  rcases x with ⟨a, b, c⟩,
  unfold f4 at h,
  have := padic_val_nat_seven_aux a b c,
  rw h at this,
  simpa using this,
end

lemma exercise05inj : ¬ (injective f5) :=
begin
  intro h,
  unfold injective at h,
  have hp : f5 ⟨1,1,1⟩ = f5 ⟨2,2,0⟩,
  {unfold f5 at *, norm_num},
  specialize h hp,
  simpa using h,
end

lemma padic_val_nat_five_aux_ (a b c : ℕ) : padic_val_nat 5 (2 ^ a * 3 ^ b * 6 ^ c) = 0 :=
begin
  haveI : fact (nat.prime 5) := fact.mk nat.prime_five,
  rw [padic_val_nat.mul 5 (mul_ne_zero _ _), padic_val_nat.mul, padic_val_nat.eq_zero_of_not_dvd,
    padic_val_nat.eq_zero_of_not_dvd, padic_val_nat.eq_zero_of_not_dvd],
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_five h,
    norm_num at h, },
  { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_five h,
    norm_num at h, },
    { intro h,
    replace h := nat.prime.dvd_of_dvd_pow nat.prime_five h,
    norm_num at h, },
  all_goals {exact pow_ne_zero _ (by norm_num)},
end

lemma exercise05surj : ¬ (surjective f5) :=
begin
  intro h,
  specialize h 5,
  cases h with x h,
  rcases x with ⟨a, b, c⟩,
  unfold f5 at h,
  have := padic_val_nat_five_aux_ a b c,
  rw h at this,
  simpa using this,
end

lemma exercise06inj : injective f6 :=
begin
  intros a b hab,
  unfold f6 at hab,
  revert hab,
  apply quotient.induction_on₂ a b,
  intros x y hab,
  simp [s_def] at hab,
  rw [quotient.eq, s_def],
  convert int.modeq.sub_right 1 hab; simp,
end

lemma exercise06surj : surjective f6 :=
begin
  intro y,
  apply quotient.induction_on y,
  clear y,
  intro a,
  use ⟦a-1⟧,
  unfold f6,
  simp,
end


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

import tactic
import data.zmod.basic

/-

# Prove that 19 ∣ 2^(2^(6k+2)) + 3 for k = 0,1,2,...

This is the fifth question in Sierpinski's book "250 elementary problems
in number theory".

thoughts

if a(k)=2^(2^(6k+2))
then a(k+1)=2^(2^6*2^(6k+2))=a(k)^64

Note that 16^64 is 16 mod 19 according to a brute force calculation
and so all of the a(k) are 16 mod 19 and we're done

-/

lemma sixteen_pow_sixtyfour_mod_nineteen : (16 : zmod 19)^64 = 16 :=
begin
  refl,
end

example (k : ℕ) : 19 ∣ 2^(2^(6*k+2))+3 :=
begin
  induction k with d hd,
  { refl },
  have h : 2 ^ 2 ^ (6 * d.succ + 2) = (2 ^ 2 ^ (6 * d + 2)) ^ 64,
  { ring_exp },
  rw [← zmod.nat_coe_zmod_eq_zero_iff_dvd, nat.cast_add, add_eq_zero_iff_eq_neg] at hd ⊢,
  rw h,
  rw nat.cast_pow,
  rw hd,
  convert sixteen_pow_sixtyfour_mod_nineteen,
end


/-
Copyright (c) 2021 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa
-/
import algebra.group.commute
import algebra.order.monoid.lemmas
import algebra.group_with_zero.basic

/-!
# Regular elements

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

We introduce left-regular, right-regular and regular elements, along with their `to_additive`
analogues add-left-regular, add-right-regular and add-regular elements.

By definition, a regular element in a commutative ring is a non-zero divisor.
Lemma `is_regular_of_ne_zero` implies that every non-zero element of an integral domain is regular.
Since it assumes that the ring is a `cancel_monoid_with_zero` it applies also, for instance, to `ℕ`.

The lemmas in Section `mul_zero_class` show that the `0` element is (left/right-)regular if and
only if the `mul_zero_class` is trivial.  This is useful when figuring out stopping conditions for
regular sequences: if `0` is ever an element of a regular sequence, then we can extend the sequence
by adding one further `0`.

The final goal is to develop part of the API to prove, eventually, results about non-zero-divisors.
-/
variables {R : Type*}

section has_mul

variables [has_mul R]

/-- A left-regular element is an element `c` such that multiplication on the left by `c`
is injective. -/
@[to_additive "An add-left-regular element is an element `c` such that addition on the left by `c`
is injective. -/
"]
def is_left_regular (c : R) := ((*) c).injective

/-- A right-regular element is an element `c` such that multiplication on the right by `c`
is injective. -/
@[to_additive "An add-right-regular element is an element `c` such that addition on the right by `c`
is injective."]
def is_right_regular (c : R) := (* c).injective

/-- An add-regular element is an element `c` such that addition by `c` both on the left and
on the right is injective. -/
structure is_add_regular {R : Type*} [has_add R] (c : R) : Prop :=
(left : is_add_left_regular c)
(right : is_add_right_regular c)

/-- A regular element is an element `c` such that multiplication by `c` both on the left and
on the right is injective. -/
structure is_regular (c : R) : Prop :=
(left : is_left_regular c)
(right : is_right_regular c)

attribute [to_additive] is_regular

@[to_additive]
protected lemma mul_le_cancellable.is_left_regular [partial_order R] {a : R}
  (ha : mul_le_cancellable a) : is_left_regular a :=
ha.injective

lemma is_left_regular.right_of_commute {a : R} (ca : ∀ b, commute a b)
  (h : is_left_regular a) : is_right_regular a :=
λ x y xy, h $ (ca x).trans $ xy.trans $ (ca y).symm

lemma commute.is_regular_iff {a : R} (ca : ∀ b, commute a b) :
  is_regular a ↔ is_left_regular a :=
⟨λ h, h.left, λ h, ⟨h, h.right_of_commute ca⟩⟩

end has_mul

section semigroup

variables [semigroup R] {a b : R}

/-- In a semigroup, the product of left-regular elements is left-regular. -/
@[to_additive "In an additive semigroup, the sum of add-left-regular elements is add-left.regular."]
lemma is_left_regular.mul (lra : is_left_regular a) (lrb : is_left_regular b) :
  is_left_regular (a * b) :=
show function.injective ((*) (a * b)), from (comp_mul_left a b) ▸ lra.comp lrb

/-- In a semigroup, the product of right-regular elements is right-regular. -/
@[to_additive
"In an additive semigroup, the sum of add-right-regular elements is add-right-regular."]
lemma is_right_regular.mul (rra : is_right_regular a) (rrb : is_right_regular b) :
  is_right_regular (a * b) :=
show function.injective (* (a * b)), from (comp_mul_right b a) ▸ rrb.comp rra

/--  If an element `b` becomes left-regular after multiplying it on the left by a left-regular
element, then `b` is left-regular. -/
@[to_additive "If an element `b` becomes add-left-regular after adding to it on the left a
add-left-regular element, then `b` is add-left-regular."]
lemma is_left_regular.of_mul (ab : is_left_regular (a * b)) :
  is_left_regular b :=
function.injective.of_comp (by rwa comp_mul_left a b)

/--  An element is left-regular if and only if multiplying it on the left by a left-regular element
is left-regular. -/
@[simp, to_additive "An element is add-left-regular if and only if adding to it on the left a
add-left-regular element is add-left-regular."]
lemma mul_is_left_regular_iff (b : R) (ha : is_left_regular a) :
  is_left_regular (a * b) ↔ is_left_regular b :=
⟨λ ab, is_left_regular.of_mul ab, λ ab, is_left_regular.mul ha ab⟩

/--  If an element `b` becomes right-regular after multiplying it on the right by a right-regular
element, then `b` is right-regular. -/
@[to_additive "If an element `b` becomes add-right-regular after adding to it on the right a
add-right-regular element, then `b` is add-right-regular."]
lemma is_right_regular.of_mul (ab : is_right_regular (b * a)) :
  is_right_regular b :=
begin
  refine λ x y xy, ab (_ : x * (b * a) = y * (b * a)),
  rw [← mul_assoc, ← mul_assoc],
  exact congr_fun (congr_arg (*) xy) a,
end

/--  An element is right-regular if and only if multiplying it on the right with a right-regular
element is right-regular. -/
@[simp, to_additive "An element is add-right-regular if and only if adding it on the right to a
add-right-regular element is add-right-regular."]
lemma mul_is_right_regular_iff (b : R) (ha : is_right_regular a) :
  is_right_regular (b * a) ↔ is_right_regular b :=
⟨λ ab, is_right_regular.of_mul ab, λ ab, is_right_regular.mul ab ha⟩

/--  Two elements `a` and `b` are regular if and only if both products `a * b` and `b * a`
are regular. -/
@[to_additive "Two elements `a` and `b` are add-regular if and only if both sums `a + b` and `b + a`
are add-regular."]
lemma is_regular_mul_and_mul_iff :
  is_regular (a * b) ∧ is_regular (b * a) ↔ is_regular a ∧ is_regular b :=
begin
  refine ⟨_, _⟩,
  { rintros ⟨ab, ba⟩,
    exact ⟨⟨is_left_regular.of_mul ba.left, is_right_regular.of_mul ab.right⟩,
      ⟨is_left_regular.of_mul ab.left, is_right_regular.of_mul ba.right⟩⟩ },
  { rintros ⟨ha, hb⟩,
    exact ⟨⟨(mul_is_left_regular_iff _ ha.left).mpr hb.left,
        (mul_is_right_regular_iff _ hb.right).mpr ha.right⟩,
      ⟨(mul_is_left_regular_iff _ hb.left).mpr ha.left,
        (mul_is_right_regular_iff _ ha.right).mpr hb.right⟩⟩ }
end

/--  The "most used" implication of `mul_and_mul_iff`, with split hypotheses, instead of `∧`. -/
@[to_additive "The \"most used\" implication of `add_and_add_iff`, with split hypotheses,
instead of `∧`."]
lemma is_regular.and_of_mul_of_mul (ab : is_regular (a * b)) (ba : is_regular (b * a)) :
  is_regular a ∧ is_regular b :=
is_regular_mul_and_mul_iff.mp ⟨ab, ba⟩

end semigroup

section mul_zero_class

variables [mul_zero_class R] {a b : R}

/--  The element `0` is left-regular if and only if `R` is trivial. -/
lemma is_left_regular.subsingleton (h : is_left_regular (0 : R)) : subsingleton R :=
⟨λ a b, h $ eq.trans (zero_mul a) (zero_mul b).symm⟩

/--  The element `0` is right-regular if and only if `R` is trivial. -/
lemma is_right_regular.subsingleton (h : is_right_regular (0 : R)) : subsingleton R :=
⟨λ a b, h $ eq.trans (mul_zero a) (mul_zero b).symm⟩

/--  The element `0` is regular if and only if `R` is trivial. -/
lemma is_regular.subsingleton (h : is_regular (0 : R)) : subsingleton R :=
h.left.subsingleton

/--  The element `0` is left-regular if and only if `R` is trivial. -/
lemma is_left_regular_zero_iff_subsingleton : is_left_regular (0 : R) ↔ subsingleton R :=
⟨λ h, h.subsingleton, λ H a b h, @subsingleton.elim _ H a b⟩

/--  In a non-trivial `mul_zero_class`, the `0` element is not left-regular. -/
lemma not_is_left_regular_zero_iff : ¬ is_left_regular (0 : R) ↔ nontrivial R :=
begin
  rw [nontrivial_iff, not_iff_comm, is_left_regular_zero_iff_subsingleton, subsingleton_iff],
  push_neg,
  exact iff.rfl
end

/--  The element `0` is right-regular if and only if `R` is trivial. -/
lemma is_right_regular_zero_iff_subsingleton : is_right_regular (0 : R) ↔ subsingleton R :=
⟨λ h, h.subsingleton, λ H a b h, @subsingleton.elim _ H a b⟩

/--  In a non-trivial `mul_zero_class`, the `0` element is not right-regular. -/
lemma not_is_right_regular_zero_iff : ¬ is_right_regular (0 : R) ↔ nontrivial R :=
begin
  rw [nontrivial_iff, not_iff_comm, is_right_regular_zero_iff_subsingleton, subsingleton_iff],
  push_neg,
  exact iff.rfl
end

/--  The element `0` is regular if and only if `R` is trivial. -/
lemma is_regular_iff_subsingleton : is_regular (0 : R) ↔ subsingleton R :=
⟨λ h, h.left.subsingleton,
 λ h, ⟨is_left_regular_zero_iff_subsingleton.mpr h, is_right_regular_zero_iff_subsingleton.mpr h⟩⟩

/-- A left-regular element of a `nontrivial` `mul_zero_class` is non-zero. -/
lemma is_left_regular.ne_zero [nontrivial R] (la : is_left_regular a) : a ≠ 0 :=
begin
  rintro rfl,
  rcases exists_pair_ne R with ⟨x, y, xy⟩,
  refine xy (la _),
  rw [zero_mul, zero_mul]
end

/-- A right-regular element of a `nontrivial` `mul_zero_class` is non-zero. -/
lemma is_right_regular.ne_zero [nontrivial R] (ra : is_right_regular a) : a ≠ 0 :=
begin
  rintro rfl,
  rcases exists_pair_ne R with ⟨x, y, xy⟩,
  refine xy (ra (_ : x * 0 = y * 0)),
  rw [mul_zero, mul_zero]
end

/-- A regular element of a `nontrivial` `mul_zero_class` is non-zero. -/
lemma is_regular.ne_zero [nontrivial R] (la : is_regular a) : a ≠ 0 :=
la.left.ne_zero

/--  In a non-trivial ring, the element `0` is not left-regular -- with typeclasses. -/
lemma not_is_left_regular_zero [nR : nontrivial R] : ¬ is_left_regular (0 : R) :=
not_is_left_regular_zero_iff.mpr nR

/--  In a non-trivial ring, the element `0` is not right-regular -- with typeclasses. -/
lemma not_is_right_regular_zero [nR : nontrivial R] : ¬ is_right_regular (0 : R) :=
not_is_right_regular_zero_iff.mpr nR

/--  In a non-trivial ring, the element `0` is not regular -- with typeclasses. -/
lemma not_is_regular_zero [nontrivial R] : ¬ is_regular (0 : R) :=
λ h, is_regular.ne_zero h rfl

end mul_zero_class

section mul_one_class

variable [mul_one_class R]

/--  If multiplying by `1` on either side is the identity, `1` is regular. -/
@[to_additive "If adding `0` on either side is the identity, `0` is regular."]
lemma is_regular_one : is_regular (1 : R) :=
⟨λ a b ab, (one_mul a).symm.trans (eq.trans ab (one_mul b)),
  λ a b ab, (mul_one a).symm.trans (eq.trans ab (mul_one b))⟩

end mul_one_class

section comm_semigroup

variables [comm_semigroup R] {a b : R}

/--  A product is regular if and only if the factors are. -/
@[to_additive "A sum is add-regular if and only if the summands are."]
lemma is_regular_mul_iff : is_regular (a * b) ↔ is_regular a ∧ is_regular b :=
begin
  refine iff.trans _ is_regular_mul_and_mul_iff,
  refine ⟨λ ab, ⟨ab, by rwa mul_comm⟩, λ rab, rab.1⟩
end

end comm_semigroup

section monoid

variables [monoid R] {a b : R}

/-- An element admitting a left inverse is left-regular. -/
@[to_additive "An element admitting a left additive opposite is add-left-regular."]
lemma is_left_regular_of_mul_eq_one (h : b * a = 1) : is_left_regular a :=
@is_left_regular.of_mul R _ _ _ (by { rw h, exact is_regular_one.left })

/-- An element admitting a right inverse is right-regular. -/
@[to_additive "An element admitting a right additive opposite is add-right-regular."]
lemma is_right_regular_of_mul_eq_one (h : a * b = 1) : is_right_regular a :=
is_right_regular.of_mul (by { rw h, exact is_regular_one.right })

/-- If `R` is a monoid, an element in `Rˣ` is regular. -/
@[to_additive "If `R` is an additive monoid, an element in `add_units R` is add-regular."]
lemma units.is_regular (a : Rˣ) : is_regular (a : R) :=
⟨is_left_regular_of_mul_eq_one a.inv_mul, is_right_regular_of_mul_eq_one a.mul_inv⟩

/-- A unit in a monoid is regular. -/
@[to_additive "An additive unit in an additive monoid is add-regular."]
lemma is_unit.is_regular (ua : is_unit a) : is_regular a :=
begin
  rcases ua with ⟨a, rfl⟩,
  exact units.is_regular a,
end

end monoid

/--  Elements of a left cancel semigroup are left regular. -/
@[to_additive "Elements of an add left cancel semigroup are add-left-regular."]
lemma is_left_regular_of_left_cancel_semigroup [left_cancel_semigroup R] (g : R) :
  is_left_regular g :=
mul_right_injective g

/--  Elements of a right cancel semigroup are right regular. -/
@[to_additive "Elements of an add right cancel semigroup are add-right-regular"]
lemma is_right_regular_of_right_cancel_semigroup [right_cancel_semigroup R] (g : R) :
  is_right_regular g :=
mul_left_injective g

section cancel_monoid

variables [cancel_monoid R]

/--  Elements of a cancel monoid are regular.  Cancel semigroups do not appear to exist. -/
@[to_additive
"Elements of an add cancel monoid are regular.  Add cancel semigroups do not appear to exist."]
lemma is_regular_of_cancel_monoid (g : R) : is_regular g :=
⟨mul_right_injective g, mul_left_injective g⟩

end cancel_monoid

section cancel_monoid_with_zero

variables [cancel_monoid_with_zero R] {a : R}

/--  Non-zero elements of an integral domain are regular. -/
lemma is_regular_of_ne_zero (a0 : a ≠ 0) : is_regular a :=
⟨λ b c, (mul_right_inj' a0).mp, λ b c, (mul_left_inj' a0).mp⟩

/-- In a non-trivial integral domain, an element is regular iff it is non-zero. -/
lemma is_regular_iff_ne_zero [nontrivial R] : is_regular a ↔ a ≠ 0 :=
⟨is_regular.ne_zero, is_regular_of_ne_zero⟩

end cancel_monoid_with_zero


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

import tactic -- imports all the Lean tactics
import data.real.basic -- imports the real numbers
import solutions.section02reals.sheet3 -- import the definition of `tends_to` from a previous sheet

-- you can maybe do this one now
theorem tends_to_neg {a : ℕ → ℝ} {t : ℝ} (ha : tends_to a t) :
  tends_to (λ n, - a n) (-t) :=
begin
  sorry,
end

/-
`tends_to_add` is quite a challenge. In a few weeks' time I'll
show you a two-line proof using filters, but right now
as you're still learning I think it's important that you
try and suffer and struggle through the first principles proof.
BIG piece of advice: write down a complete maths proof first,
with all the details there. Then, once you know the maths
proof, try translating it into Lean. Note that a bunch
of the results we proved in sheet 4 will be helpful.
-/

/-- If `a(n)` tends to `t` and `b(n)` tends to `u` then `a(n) + b(n)`
tends to `t + u`. -/
theorem tends_to_add {a b : ℕ → ℝ} {t u : ℝ}
  (ha : tends_to a t) (hb : tends_to b u) :
  tends_to (λ n, a n + b n) (t + u) :=
begin
  sorry
end

/-- If `a(n)` tends to t and `b(n)` tends to `u` then `a(n) - b(n)`
tends to `t - u`. -/
theorem tends_to_sub {a b : ℕ → ℝ} {t u : ℝ}
  (ha : tends_to a t) (hb : tends_to b u) :
  tends_to (λ n, a n - b n) (t - u) :=
begin
  -- this one follows without too much trouble from earlier results.
  sorry
end


-- need access to many useful tactics
import tactic

-- need injective functions
open function

-- Let X, Y, Z be types and
-- let f : X → Y and g : Y → Z be
-- functions between these types
variables (X Y Z : Type)
  (f : X → Y) (g : Y → Z)

-- Theorem: if f and g are
-- injective, then so is g ∘ f.
theorem injective_comp :
  injective f ∧ injective g →
  injective (g ∘ f) :=
begin
  -- assume f and g are injective.
  rintro ⟨f_inj, g_inj⟩,
  -- We want to prove g ∘ f is
  -- injective. So say a,b ∈ X and
  -- assume g(f(a))=g(f(b)).
  intros a b hgf,
  -- We want to prove that a = b. By
  -- injectivity of f, it suffices to
  -- prove that f(a)=f(b).
  apply f_inj,
  -- By injectivity of g, it suffices
  -- to prove g(f(a))=g(f(b)).
  apply g_inj,
  -- But this is an assumption.
  assumption,
end


-- Razonamiento con tipos enumerados: Movimientos
-- ==============================================

import tactic

-- ----------------------------------------------------
-- Nota. Usaremos los tipo Pos (como una abreviatura
-- de pares de enteros para representar posiciones) y
-- Direccion (como un tipo enumerado con las cuatro
-- direcciones) y la función opuesta, definidas
-- anteriormente
-- ----------------------------------------------------

def Pos : Type := ℤ × ℤ

inductive Direccion : Type
| Izquierda : Direccion
| Derecha   : Direccion
| Arriba    : Direccion
| Abajo     : Direccion

namespace Direccion

@[simp]
def opuesta : Direccion → Direccion
| Izquierda := Derecha
| Derecha   := Izquierda
| Arriba    := Abajo
| Abajo     := Arriba

-- ----------------------------------------------------
-- Ejercicio ?. Definir la función
--    movimiento : Direccion → Pos → Pos
-- tal que (movimiento d p) es la posición alcanzada
-- al dar un paso en la dirección d a partir de la
-- posición p. Por ejemplo,
--    movimiento Arriba (2,5) = (2, 6)
-- ----------------------------------------------------

@[simp]
def movimiento : Direccion → Pos → Pos
| Izquierda (x,y) := (x-1,y)
| Derecha   (x,y) := (x+1,y)
| Arriba    (x,y) := (x,y+1)
| Abajo     (x,y) := (x,y-1)

-- #eval movimiento Arriba (2,5)
-- Da: (2, 6)

-- ----------------------------------------------------
-- Ejercicio ?. Definir la función
--    movimientos : list Direccion → Pos → Pos
-- tal que (movimientos ms p) es la posición obtenida
-- aplicando la lista de movimientos ms a la posición
-- p. Por ejemplo,
--    movimientos [Arriba, Izquierda] (2,5) = (1,6)
-- ----------------------------------------------------

def movimientos : list Direccion → Pos → Pos
| []        p := p
| (m :: ms) p := movimientos ms (movimiento m p)

-- #eval movimientos [Arriba, Izquierda] (2,5)
-- Da:  (1,6)

-- ----------------------------------------------------
-- Ejercicio ?. Demostrar que para cada dirección d
-- existe una dirección d' tal que para toda posición p,
--    movimiento d' (movimiento d p) = p
-- ----------------------------------------------------

-- 1ª demostración
example :
  ∀ d, ∃ d', ∀ p, movimiento d' (movimiento d p) = p :=
begin
  intro d,
  use opuesta d,
  rintro ⟨x,y⟩,
  cases d,
  { calc movimiento (opuesta Izquierda) (movimiento Izquierda (x,y))
         = movimiento (opuesta Izquierda) (x-1,y)       :by simp [movimiento]
     ... = movimiento Derecha (x-1,y)                   :by simp [opuesta]
     ... = (x-1+1,y)                                    :by simp [movimiento]
     ... = (x,y)                                        :by simp },
  { calc movimiento (opuesta Derecha) (movimiento Derecha (x,y))
         = movimiento (opuesta Derecha) (x+1,y)         :by simp [movimiento]
     ... = movimiento Izquierda (x+1,y)                 :by simp [opuesta]
     ... = (x+1-1,y)                                    :by simp [movimiento]
     ... = (x,y)                                        :by simp },
  { calc movimiento (opuesta Arriba) (movimiento Arriba (x,y))
         = movimiento (opuesta Arriba) (x,y+1)          :by simp [movimiento]
     ... = movimiento Abajo (x,y+1)                     :by simp [opuesta]
     ... = (x,y+1-1)                                    :by simp [movimiento]
     ... = (x,y)                                        :by simp },
  { calc movimiento (opuesta Abajo) (movimiento Abajo (x,y))
         = movimiento (opuesta Abajo) (x,y-1)           :by simp [movimiento]
     ... = movimiento Arriba (x,y-1)                    :by simp [opuesta]
     ... = (x,y-1+1)                                    :by simp [movimiento]
     ... = (x,y)                                        :by simp },
end

-- 2ª demostración
example :
  ∀ d, ∃ d', ∀ p, movimiento d' (movimiento d p) = p :=
begin
  intro d,
  use opuesta d,
  rintro ⟨x,y⟩,
  cases d,
  { calc movimiento (opuesta Izquierda) (movimiento Izquierda (x,y))
         = movimiento (opuesta Izquierda) (x-1,y)       :by simp
     ... = movimiento Derecha (x-1,y)                   :by simp
     ... = (x-1+1,y)                                    :by simp
     ... = (x,y)                                        :by simp },
  { calc movimiento (opuesta Derecha) (movimiento Derecha (x,y))
         = movimiento (opuesta Derecha) (x+1,y)         :by simp
     ... = movimiento Izquierda (x+1,y)                 :by simp
     ... = (x+1-1,y)                                    :by simp
     ... = (x,y)                                        :by simp },
  { calc movimiento (opuesta Arriba) (movimiento Arriba (x,y))
         = movimiento (opuesta Arriba) (x,y+1)          :by simp
     ... = movimiento Abajo (x,y+1)                     :by simp
     ... = (x,y+1-1)                                    :by simp
     ... = (x,y)                                        :by simp },
  { calc movimiento (opuesta Abajo) (movimiento Abajo (x,y))
         = movimiento (opuesta Abajo) (x,y-1)           :by simp
     ... = movimiento Arriba (x,y-1)                    :by simp
     ... = (x,y-1+1)                                    :by simp
     ... = (x,y)                                        :by simp },
end

-- 3ª demostración
example :
  ∀ d, ∃ d', ∀ p, movimiento d' (movimiento d p) = p :=
begin
  intro d,
  use opuesta d,
  rintro ⟨x,y⟩,
  cases d,
  { simp, },
  { simp, },
  { simp, },
  { simp, },
end

-- 4ª demostración
example :
  ∀ d, ∃ d', ∀ p, movimiento d' (movimiento d p) = p :=
begin
  intro d,
  use opuesta d,
  rintro ⟨x,y⟩,
  cases d ;
  simp,
end

-- 5ª demostración
example :
  ∀ d, ∃ d', ∀ p, movimiento d' (movimiento d p) = p :=
assume d,
exists.intro (opuesta d)
  (assume ⟨x,y⟩,
   show movimiento (opuesta d) (movimiento d (x,y)) = (x,y), from
     Direccion.cases_on d
       (calc movimiento (opuesta Izquierda) (movimiento Izquierda (x,y))
             = movimiento (opuesta Izquierda) (x-1,y)       :by simp
         ... = movimiento Derecha (x-1,y)                   :by simp
         ... = (x-1+1,y)                                    :by simp
         ... = (x,y)                                        :by simp)
       (calc movimiento (opuesta Derecha) (movimiento Derecha (x,y))
             = movimiento (opuesta Derecha) (x+1,y)         :by simp
         ... = movimiento Izquierda (x+1,y)                 :by simp
         ... = (x+1-1,y)                                    :by simp
         ... = (x,y)                                        :by simp )
       (calc movimiento (opuesta Arriba) (movimiento Arriba (x,y))
             = movimiento (opuesta Arriba) (x,y+1)          :by simp
         ... = movimiento Abajo (x,y+1)                     :by simp
         ... = (x,y+1-1)                                    :by simp
         ... = (x,y)                                        :by simp)
       (calc movimiento (opuesta Abajo) (movimiento Abajo (x,y))
             = movimiento (opuesta Abajo) (x,y-1)           :by simp
         ... = movimiento Arriba (x,y-1)                    :by simp
         ... = (x,y-1+1)                                    :by simp
         ... = (x,y)                                        :by simp))

-- 6ª demostración
example :
  ∀ d, ∃ d', ∀ p, movimiento d' (movimiento d p) = p :=
assume d,
exists.intro (opuesta d)
  (assume ⟨x,y⟩,
   show movimiento (opuesta d) (movimiento d (x,y)) = (x,y), from
     Direccion.cases_on d
       (by simp)
       (by simp)
       (by simp)
       (by simp))

-- 7ª demostración
example :
  ∀ d, ∃ d', ∀ p, movimiento d' (movimiento d p) = p :=
assume d,
exists.intro (opuesta d)
  (λ ⟨x,y⟩, Direccion.cases_on d (by simp) (by simp) (by simp) (by simp))

-- 8ª demostración
example :
  ∀ d, ∃ d', ∀ p, movimiento d' (movimiento d p) = p :=
λ d, exists.intro (opuesta d)
       (λ ⟨x,y⟩, Direccion.cases_on d (by simp) (by simp) (by simp) (by simp))

end Direccion


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

import solutions.section14polynomials.sheet04more_aux_ideal

/-

Recall that we know `aux_ideal I n` is an increasing function of `n`.
In this file we define `aux_ideal2 I` to be the union over all `n`
of `aux_ideal I n`. Note that it is an ideal of `R`, so if `R` is
Noetherian then it's going to be finitely-generated. This is a key
ingredient im the proof.

-/

variables {R : Type} [comm_ring R] (I : ideal (polynomial R))

open polynomial

/-

Recall the key API for `⋃` is `set.mem_Union`
-/

/-- An auxiliary ideal used in the proof of Hilbert's Basis Theorem.
It's equal to the elements of `R` which are either 0, or the
leading coefficient of an element of `I`. Showing that this is an
ideal is made much easier by the fact that it can also be thought
of as a union of the increasing sequence of ideals `aux_ideal I n`. -/
def aux_ideal2 (I : ideal (polynomial R)) : ideal R :=
{ carrier := ⋃ n, aux_ideal I n,
  zero_mem' := begin
    rw set.mem_Union,
    use 37,
    exact ideal.zero_mem (aux_ideal I 37),
  end,
  add_mem' := begin
    intros f g hf hg,
    rw set.mem_Union at hf hg ⊢,
    rcases hf with ⟨a, ha⟩,
    rcases hg with ⟨b, hb⟩,
    use max a b,
    apply (aux_ideal I (max a b)).add_mem,
    { exact aux_ideal.mono _ (le_max_left a b) ha },
    { exact aux_ideal.mono _ (le_max_right a b) hb },
  end,
  smul_mem' := begin
    intros r f hf,
    rw set.mem_Union at hf ⊢,
    cases hf with i hi,
    use i,
    refine submodule.smul_mem (aux_ideal I i) r hi,
  end }

namespace aux_ideal2

-- we make some helpful API.

lemma mem (I : ideal (polynomial R)) (j : R) (hj : j ∈ aux_ideal2 I) :
  ∃ m, j ∈ aux_ideal I m :=
set.mem_Union.1 hj

lemma mem_iff (I : ideal (polynomial R)) (j : R) :
 j ∈ aux_ideal2 I ↔ ∃ m, j ∈ aux_ideal I m :=
set.mem_Union

/-

Assume `R` is Noetherian. Because `aux_ideal2 I` is finitely-generated,
the increasing chain of ideals `aux_ideal I 0 ≤ aux_ideal I 1 ≤ aux_ideal I 2 ≤ ...`
must stabilise.

Helpful API: `finset.sup`. Don't forget to use the `classical` tactic
if you run into decidability issues.

-/
lemma canonical_N (hR : is_noetherian_ring R) :
  ∃ N, ∀ m, N ≤ m → aux_ideal2 I = aux_ideal I m :=
begin
  -- Q -- can this be done with `is_noetherian_iff_well_founded`?
  obtain ⟨S, hS⟩ := (is_noetherian_ring_iff_ideal_fg R).1 hR (aux_ideal2 I),
  have hSmem : ∀ r : R, r ∈ S → r ∈ aux_ideal2 I,
  { rw ← hS, intros r hr, exact ideal.subset_span hr },
  choose g hg using mem I,
  classical,
  use finset.sup S (λ r, if hr : r ∈ aux_ideal2 I then g r hr else 37),
  intros m hm,
  apply le_antisymm,
  { rw ← hS,
    rw ideal.span_le,
    intros r hrS,
    rw finset.sup_le_iff at hm,
    specialize hm r hrS,
    have hraux := hSmem _ hrS,
    rw dif_pos hraux at hm,
    apply aux_ideal.mono I hm,
    apply hg },
  { intros r hr,
    rw mem_iff,
    use m,
    assumption },
end

end aux_ideal2


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Chris Hughes, Mario Carneiro
-/
import algebra.associated
import linear_algebra.basic
import order.zorn
import order.atoms
import order.compactly_generated
import tactic.abel
import data.nat.choose.sum
import linear_algebra.finsupp
/-!

# Ideals over a ring

This file defines `ideal R`, the type of ideals over a commutative ring `R`.

## Implementation notes

`ideal R` is implemented using `submodule R R`, where `•` is interpreted as `*`.

## TODO

Support one-sided ideals, and ideals over non-commutative rings.
-/

universes u v w
variables {α : Type u} {β : Type v}
open set function

open_locale classical big_operators pointwise

/-- A (left) ideal in a semiring `R` is an additive submonoid `s` such that
`a * b ∈ s` whenever `b ∈ s`. If `R` is a ring, then `s` is an additive subgroup.  -/
@[reducible] def ideal (R : Type u) [semiring R] := submodule R R

section semiring

namespace ideal
variables [semiring α] (I : ideal α) {a b : α}

protected lemma zero_mem : (0 : α) ∈ I := I.zero_mem

protected lemma add_mem : a ∈ I → b ∈ I → a + b ∈ I := I.add_mem

variables (a)
lemma mul_mem_left : b ∈ I → a * b ∈ I := I.smul_mem a
variables {a}

@[ext] lemma ext {I J : ideal α} (h : ∀ x, x ∈ I ↔ x ∈ J) : I = J :=
submodule.ext h

lemma sum_mem (I : ideal α) {ι : Type*} {t : finset ι} {f : ι → α} :
  (∀c∈t, f c ∈ I) → (∑ i in t, f i) ∈ I := submodule.sum_mem I

theorem eq_top_of_unit_mem
  (x y : α) (hx : x ∈ I) (h : y * x = 1) : I = ⊤ :=
eq_top_iff.2 $ λ z _, calc
    z = z * (y * x) : by simp [h]
  ... = (z * y) * x : eq.symm $ mul_assoc z y x
  ... ∈ I : I.mul_mem_left _ hx

theorem eq_top_of_is_unit_mem {x} (hx : x ∈ I) (h : is_unit x) : I = ⊤ :=
let ⟨y, hy⟩ := h.exists_left_inv in eq_top_of_unit_mem I x y hx hy

theorem eq_top_iff_one : I = ⊤ ↔ (1:α) ∈ I :=
⟨by rintro rfl; trivial,
 λ h, eq_top_of_unit_mem _ _ 1 h (by simp)⟩

theorem ne_top_iff_one : I ≠ ⊤ ↔ (1:α) ∉ I :=
not_congr I.eq_top_iff_one

@[simp]
theorem unit_mul_mem_iff_mem {x y : α} (hy : is_unit y) : y * x ∈ I ↔ x ∈ I :=
begin
  refine ⟨λ h, _, λ h, I.mul_mem_left y h⟩,
  obtain ⟨y', hy'⟩ := hy.exists_left_inv,
  have := I.mul_mem_left y' h,
  rwa [← mul_assoc, hy', one_mul] at this,
end

/-- The ideal generated by a subset of a ring -/
def span (s : set α) : ideal α := submodule.span α s

@[simp] lemma submodule_span_eq {s : set α} :
  submodule.span α s = ideal.span s :=
rfl

@[simp] lemma span_empty : span (∅ : set α) = ⊥ := submodule.span_empty

@[simp] lemma span_univ : span (set.univ : set α) = ⊤ := submodule.span_univ

lemma span_union (s t : set α) : span (s ∪ t) = span s ⊔ span t :=
submodule.span_union _ _

lemma span_Union {ι} (s : ι → set α) : span (⋃ i, s i) = ⨆ i, span (s i) :=
submodule.span_Union _

lemma mem_span {s : set α} (x) : x ∈ span s ↔ ∀ p : ideal α, s ⊆ p → x ∈ p :=
mem_Inter₂

lemma subset_span {s : set α} : s ⊆ span s := submodule.subset_span

lemma span_le {s : set α} {I} : span s ≤ I ↔ s ⊆ I := submodule.span_le

lemma span_mono {s t : set α} : s ⊆ t → span s ≤ span t := submodule.span_mono

@[simp] lemma span_eq : span (I : set α) = I := submodule.span_eq _

@[simp] lemma span_singleton_one : span ({1} : set α) = ⊤ :=
(eq_top_iff_one _).2 $ subset_span $ mem_singleton _

lemma mem_span_insert {s : set α} {x y} :
  x ∈ span (insert y s) ↔ ∃ a (z ∈ span s), x = a * y + z := submodule.mem_span_insert

lemma mem_span_singleton' {x y : α} :
  x ∈ span ({y} : set α) ↔ ∃ a, a * y = x := submodule.mem_span_singleton

lemma span_insert (x) (s : set α) : span (insert x s) = span ({x} : set α) ⊔ span s :=
submodule.span_insert x s

lemma span_eq_bot {s : set α} : span s = ⊥ ↔ ∀ x ∈ s, (x:α) = 0 := submodule.span_eq_bot

@[simp] lemma span_singleton_eq_bot {x} : span ({x} : set α) = ⊥ ↔ x = 0 :=
submodule.span_singleton_eq_bot

@[simp] lemma span_zero : span (0 : set α) = ⊥ := by rw [←set.singleton_zero, span_singleton_eq_bot]

@[simp] lemma span_one : span (1 : set α) = ⊤ := by rw [←set.singleton_one, span_singleton_one]

lemma span_eq_top_iff_finite (s : set α) :
  span s = ⊤ ↔ ∃ s' : finset α, ↑s' ⊆ s ∧ span (s' : set α) = ⊤ :=
begin
  simp_rw eq_top_iff_one,
  exact ⟨submodule.mem_span_finite_of_mem_span, λ ⟨s', h₁, h₂⟩, span_mono h₁ h₂⟩
end

/--
The ideal generated by an arbitrary binary relation.
-/
def of_rel (r : α → α → Prop) : ideal α :=
submodule.span α { x | ∃ (a b) (h : r a b), x + b = a }

/-- An ideal `P` of a ring `R` is prime if `P ≠ R` and `xy ∈ P → x ∈ P ∨ y ∈ P` -/
class is_prime (I : ideal α) : Prop :=
(ne_top' : I ≠ ⊤)
(mem_or_mem' : ∀ {x y : α}, x * y ∈ I → x ∈ I ∨ y ∈ I)

theorem is_prime_iff {I : ideal α} :
  is_prime I ↔ I ≠ ⊤ ∧ ∀ {x y : α}, x * y ∈ I → x ∈ I ∨ y ∈ I :=
⟨λ h, ⟨h.1, h.2⟩, λ h, ⟨h.1, h.2⟩⟩

theorem is_prime.ne_top {I : ideal α} (hI : I.is_prime) : I ≠ ⊤ := hI.1

theorem is_prime.mem_or_mem {I : ideal α} (hI : I.is_prime) :
  ∀ {x y : α}, x * y ∈ I → x ∈ I ∨ y ∈ I := hI.2

theorem is_prime.mem_or_mem_of_mul_eq_zero {I : ideal α} (hI : I.is_prime)
  {x y : α} (h : x * y = 0) : x ∈ I ∨ y ∈ I :=
hI.mem_or_mem (h.symm ▸ I.zero_mem)

theorem is_prime.mem_of_pow_mem {I : ideal α} (hI : I.is_prime)
  {r : α} (n : ℕ) (H : r^n ∈ I) : r ∈ I :=
begin
  induction n with n ih,
  { rw pow_zero at H, exact (mt (eq_top_iff_one _).2 hI.1).elim H },
  { rw pow_succ at H, exact or.cases_on (hI.mem_or_mem H) id ih }
end

lemma not_is_prime_iff {I : ideal α} : ¬ I.is_prime ↔ I = ⊤ ∨ ∃ (x ∉ I) (y ∉ I), x * y ∈ I :=
begin
  simp_rw [ideal.is_prime_iff, not_and_distrib, ne.def, not_not, not_forall, not_or_distrib],
  exact or_congr iff.rfl
    ⟨λ ⟨x, y, hxy, hx, hy⟩, ⟨x, hx, y, hy, hxy⟩, λ ⟨x, hx, y, hy, hxy⟩, ⟨x, y, hxy, hx, hy⟩⟩
end

theorem zero_ne_one_of_proper {I : ideal α} (h : I ≠ ⊤) : (0:α) ≠ 1 :=
λ hz, I.ne_top_iff_one.1 h $ hz ▸ I.zero_mem

lemma bot_prime {R : Type*} [ring R] [is_domain R] : (⊥ : ideal R).is_prime :=
⟨λ h, one_ne_zero (by rwa [ideal.eq_top_iff_one, submodule.mem_bot] at h),
 λ x y h, mul_eq_zero.mp (by simpa only [submodule.mem_bot] using h)⟩

/-- An ideal is maximal if it is maximal in the collection of proper ideals. -/
class is_maximal (I : ideal α) : Prop := (out : is_coatom I)

theorem is_maximal_def {I : ideal α} : I.is_maximal ↔ is_coatom I := ⟨λ h, h.1, λ h, ⟨h⟩⟩

theorem is_maximal.ne_top {I : ideal α} (h : I.is_maximal) : I ≠ ⊤ := (is_maximal_def.1 h).1

theorem is_maximal_iff {I : ideal α} : I.is_maximal ↔
  (1:α) ∉ I ∧ ∀ (J : ideal α) x, I ≤ J → x ∉ I → x ∈ J → (1:α) ∈ J :=
is_maximal_def.trans $ and_congr I.ne_top_iff_one $ forall_congr $ λ J,
by rw [lt_iff_le_not_le]; exact
 ⟨λ H x h hx₁ hx₂, J.eq_top_iff_one.1 $
    H ⟨h, not_subset.2 ⟨_, hx₂, hx₁⟩⟩,
  λ H ⟨h₁, h₂⟩, let ⟨x, xJ, xI⟩ := not_subset.1 h₂ in
   J.eq_top_iff_one.2 $ H x h₁ xI xJ⟩

theorem is_maximal.eq_of_le {I J : ideal α}
  (hI : I.is_maximal) (hJ : J ≠ ⊤) (IJ : I ≤ J) : I = J :=
eq_iff_le_not_lt.2 ⟨IJ, λ h, hJ (hI.1.2 _ h)⟩

instance : is_coatomic (ideal α) :=
begin
  apply complete_lattice.coatomic_of_top_compact,
  rw ←span_singleton_one,
  exact submodule.singleton_span_is_compact_element 1,
end

/-- **Krull's theorem**: if `I` is an ideal that is not the whole ring, then it is included in some
    maximal ideal. -/
theorem exists_le_maximal (I : ideal α) (hI : I ≠ ⊤) :
  ∃ M : ideal α, M.is_maximal ∧ I ≤ M :=
let ⟨m, hm⟩ := (eq_top_or_exists_le_coatom I).resolve_left hI in ⟨m, ⟨⟨hm.1⟩, hm.2⟩⟩

variables (α)

/-- Krull's theorem: a nontrivial ring has a maximal ideal. -/
theorem exists_maximal [nontrivial α] : ∃ M : ideal α, M.is_maximal :=
let ⟨I, ⟨hI, _⟩⟩ := exists_le_maximal (⊥ : ideal α) bot_ne_top in ⟨I, hI⟩

variables {α}

instance [nontrivial α] : nontrivial (ideal α) :=
begin
  rcases @exists_maximal α _ _ with ⟨M, hM, _⟩,
  exact nontrivial_of_ne M ⊤ hM
end

/-- If P is not properly contained in any maximal ideal then it is not properly contained
  in any proper ideal -/
lemma maximal_of_no_maximal {R : Type u} [comm_semiring R] {P : ideal R}
(hmax : ∀ m : ideal R, P < m → ¬is_maximal m) (J : ideal R) (hPJ : P < J) : J = ⊤ :=
begin
  by_contradiction hnonmax,
  rcases exists_le_maximal J hnonmax with ⟨M, hM1, hM2⟩,
  exact hmax M (lt_of_lt_of_le hPJ hM2) hM1,
end

theorem mem_span_pair {x y z : α} :
  z ∈ span ({x, y} : set α) ↔ ∃ a b, a * x + b * y = z :=
by simp [mem_span_insert, mem_span_singleton', @eq_comm _ _ z]

theorem is_maximal.exists_inv {I : ideal α}
  (hI : I.is_maximal) {x} (hx : x ∉ I) : ∃ y, ∃ i ∈ I, y * x + i = 1 :=
begin
  cases is_maximal_iff.1 hI with H₁ H₂,
  rcases mem_span_insert.1 (H₂ (span (insert x I)) x
    (set.subset.trans (subset_insert _ _) subset_span)
    hx (subset_span (mem_insert _ _))) with ⟨y, z, hz, hy⟩,
  refine ⟨y, z, _, hy.symm⟩,
  rwa ← span_eq I,
end

section lattice
variables {R : Type u} [semiring R]

lemma mem_sup_left {S T : ideal R} : ∀ {x : R}, x ∈ S → x ∈ S ⊔ T :=
show S ≤ S ⊔ T, from le_sup_left

lemma mem_sup_right {S T : ideal R} : ∀ {x : R}, x ∈ T → x ∈ S ⊔ T :=
show T ≤ S ⊔ T, from le_sup_right

lemma mem_Sup_of_mem {S : set (ideal R)} {s : ideal R}
  (hs : s ∈ S) : ∀ {x : R}, x ∈ s → x ∈ Sup S :=
show s ≤ Sup S, from le_Sup hs

theorem mem_Inf {s : set (ideal R)} {x : R} :
  x ∈ Inf s ↔ ∀ ⦃I⦄, I ∈ s → x ∈ I :=
⟨λ hx I his, hx I ⟨I, infi_pos his⟩, λ H I ⟨J, hij⟩, hij ▸ λ S ⟨hj, hS⟩, hS ▸ H hj⟩

@[simp] lemma mem_inf {I J : ideal R} {x : R} : x ∈ I ⊓ J ↔ x ∈ I ∧ x ∈ J := iff.rfl

@[simp] lemma mem_infi {ι : Sort*} {I : ι → ideal R} {x : R} : x ∈ infi I ↔ ∀ i, x ∈ I i :=
submodule.mem_infi _

@[simp] lemma mem_bot {x : R} : x ∈ (⊥ : ideal R) ↔ x = 0 :=
submodule.mem_bot _

end lattice

section pi
variables (ι : Type v)

/-- `I^n` as an ideal of `R^n`. -/
def pi : ideal (ι → α) :=
{ carrier := { x | ∀ i, x i ∈ I },
  zero_mem' := λ i, I.zero_mem,
  add_mem' := λ a b ha hb i, I.add_mem (ha i) (hb i),
  smul_mem' := λ a b hb i, I.mul_mem_left (a i) (hb i) }

lemma mem_pi (x : ι → α) : x ∈ I.pi ι ↔ ∀ i, x i ∈ I := iff.rfl

end pi

end ideal

end semiring

section comm_semiring

variables {a b : α}

-- A separate namespace definition is needed because the variables were historically in a different
-- order.
namespace ideal
variables [comm_semiring α] (I : ideal α)

@[simp]
theorem mul_unit_mem_iff_mem {x y : α} (hy : is_unit y) : x * y ∈ I ↔ x ∈ I :=
mul_comm y x ▸ unit_mul_mem_iff_mem I hy

lemma mem_span_singleton {x y : α} :
  x ∈ span ({y} : set α) ↔ y ∣ x :=
mem_span_singleton'.trans $ exists_congr $ λ _, by rw [eq_comm, mul_comm]

lemma span_singleton_le_span_singleton {x y : α} :
  span ({x} : set α) ≤ span ({y} : set α) ↔ y ∣ x :=
span_le.trans $ singleton_subset_iff.trans mem_span_singleton

lemma span_singleton_eq_span_singleton {α : Type u} [comm_ring α] [is_domain α] {x y : α} :
  span ({x} : set α) = span ({y} : set α) ↔ associated x y :=
begin
  rw [←dvd_dvd_iff_associated, le_antisymm_iff, and_comm],
  apply and_congr;
  rw span_singleton_le_span_singleton,
end

lemma span_singleton_mul_right_unit {a : α} (h2 : is_unit a) (x : α) :
  span ({x * a} : set α) = span {x} :=
begin
  apply le_antisymm,
  { rw span_singleton_le_span_singleton, use a},
  { rw span_singleton_le_span_singleton, rw is_unit.mul_right_dvd h2}
end

lemma span_singleton_mul_left_unit {a : α} (h2 : is_unit a) (x : α) :
  span ({a * x} : set α) = span {x} := by rw [mul_comm, span_singleton_mul_right_unit h2]

lemma span_singleton_eq_top {x} : span ({x} : set α) = ⊤ ↔ is_unit x :=
by rw [is_unit_iff_dvd_one, ← span_singleton_le_span_singleton, span_singleton_one,
  eq_top_iff]

theorem span_singleton_prime {p : α} (hp : p ≠ 0) :
  is_prime (span ({p} : set α)) ↔ prime p :=
by simp [is_prime_iff, prime, span_singleton_eq_top, hp, mem_span_singleton]

theorem is_maximal.is_prime {I : ideal α} (H : I.is_maximal) : I.is_prime :=
⟨H.1.1, λ x y hxy, or_iff_not_imp_left.2 $ λ hx, begin
  let J : ideal α := submodule.span α (insert x ↑I),
  have IJ : I ≤ J  := (set.subset.trans (subset_insert _ _) subset_span),
  have xJ : x ∈ J := ideal.subset_span (set.mem_insert x I),
  cases is_maximal_iff.1 H with _ oJ,
  specialize oJ J x IJ hx xJ,
  rcases submodule.mem_span_insert.mp oJ with ⟨a, b, h, oe⟩,
  obtain (F : y * 1 = y * (a • x + b)) := congr_arg (λ g : α, y * g) oe,
  rw [← mul_one y, F, mul_add, mul_comm, smul_eq_mul, mul_assoc],
  refine submodule.add_mem I (I.mul_mem_left a hxy) (submodule.smul_mem I y _),
  rwa submodule.span_eq at h,
end⟩

@[priority 100] -- see Note [lower instance priority]
instance is_maximal.is_prime' (I : ideal α) : ∀ [H : I.is_maximal], I.is_prime :=
is_maximal.is_prime

lemma span_singleton_lt_span_singleton [comm_ring β] [is_domain β] {x y : β} :
  span ({x} : set β) < span ({y} : set β) ↔ dvd_not_unit y x :=
by rw [lt_iff_le_not_le, span_singleton_le_span_singleton, span_singleton_le_span_singleton,
  dvd_and_not_dvd_iff]

lemma factors_decreasing [comm_ring β] [is_domain β]
  (b₁ b₂ : β) (h₁ : b₁ ≠ 0) (h₂ : ¬ is_unit b₂) :
  span ({b₁ * b₂} : set β) < span {b₁} :=
lt_of_le_not_le (ideal.span_le.2 $ singleton_subset_iff.2 $
  ideal.mem_span_singleton.2 ⟨b₂, rfl⟩) $ λ h,
h₂ $ is_unit_of_dvd_one _ $ (mul_dvd_mul_iff_left h₁).1 $
by rwa [mul_one, ← ideal.span_singleton_le_span_singleton]

variables (b)
lemma mul_mem_right (h : a ∈ I) : a * b ∈ I := mul_comm b a ▸ I.mul_mem_left b h
variables {b}

lemma pow_mem_of_mem (ha : a ∈ I) (n : ℕ) (hn : 0 < n) : a ^ n ∈ I :=
nat.cases_on n (not.elim dec_trivial) (λ m hm, (pow_succ a m).symm ▸ I.mul_mem_right (a^m) ha) hn

theorem is_prime.mul_mem_iff_mem_or_mem {I : ideal α} (hI : I.is_prime) :
  ∀ {x y : α}, x * y ∈ I ↔ x ∈ I ∨ y ∈ I :=
λ x y, ⟨hI.mem_or_mem, by { rintro (h | h), exacts [I.mul_mem_right y h, I.mul_mem_left x h] }⟩

theorem is_prime.pow_mem_iff_mem {I : ideal α} (hI : I.is_prime)
  {r : α} (n : ℕ) (hn : 0 < n) : r ^ n ∈ I ↔ r ∈ I :=
⟨hI.mem_of_pow_mem n, (λ hr, I.pow_mem_of_mem hr n hn)⟩

theorem pow_multiset_sum_mem_span_pow (s : multiset α) (n : ℕ) :
  s.sum ^ (s.card * n + 1) ∈ span ((s.map (λ x, x ^ (n + 1))).to_finset : set α) :=
begin
  induction s using multiset.induction_on with a s hs,
  { simp },
  simp only [finset.coe_insert, multiset.map_cons, multiset.to_finset_cons, multiset.sum_cons,
    multiset.card_cons, add_pow],
  refine submodule.sum_mem _ _,
  intros c hc,
  rw mem_span_insert,
  by_cases h : n+1 ≤ c,
  { refine ⟨a ^ (c - (n + 1)) * s.sum ^ ((s.card + 1) * n + 1 - c) *
      (((s.card + 1) * n + 1).choose c), 0, submodule.zero_mem _, _⟩,
    rw mul_comm _ (a ^ (n + 1)),
    simp_rw ← mul_assoc,
    rw [← pow_add, add_zero, add_tsub_cancel_of_le h], },
  { use 0,
    simp_rw [zero_mul, zero_add],
    refine ⟨_,_,rfl⟩,
    replace h : c ≤ n := nat.lt_succ_iff.mp (not_le.mp h),
    have : (s.card + 1) * n + 1 - c = s.card * n + 1 + (n - c),
    { rw [add_mul, one_mul, add_assoc, add_comm n 1, ← add_assoc, add_tsub_assoc_of_le h] },
    rw [this, pow_add],
    simp_rw [mul_assoc, mul_comm (s.sum ^ (s.card * n + 1)), ← mul_assoc],
    exact mul_mem_left _ _ hs }
end

theorem sum_pow_mem_span_pow {ι} (s : finset ι) (f : ι → α) (n : ℕ) :
  (∑ i in s, f i) ^ (s.card * n + 1) ∈ span ((λ i, f i ^ (n + 1)) '' s) :=
begin
  convert pow_multiset_sum_mem_span_pow (s.1.map f) n,
  { rw multiset.card_map, refl },
  rw [multiset.map_map, multiset.to_finset_map, finset.val_to_finset, finset.coe_image]
end

theorem span_pow_eq_top (s : set α)
  (hs : span s = ⊤) (n : ℕ) : span ((λ x, x ^ n) '' s) = ⊤ :=
begin
  rw eq_top_iff_one,
  cases n,
  { obtain rfl | ⟨x, hx⟩ := eq_empty_or_nonempty s,
    { rw [set.image_empty, hs],
      trivial },
    { exact subset_span ⟨_, hx, pow_zero _⟩ } },
  rw [eq_top_iff_one, span, finsupp.mem_span_iff_total] at hs,
  rcases hs with ⟨f, hf⟩,
  change f.support.sum (λ a, f a * a) = 1 at hf,
  have := sum_pow_mem_span_pow f.support (λ a, f a * a) n,
  rw [hf, one_pow] at this,
  refine (span_le).mpr _ this,
  rintros _ hx,
  simp_rw [finset.mem_coe, set.mem_image] at hx,
  rcases hx with ⟨x, hx, rfl⟩,
  have : span ({x ^ (n + 1)} : set α) ≤ span ((λ (x : α), x ^ (n + 1)) '' s),
  { rw [span_le, set.singleton_subset_iff],
    exact subset_span ⟨x, x.prop, rfl⟩ },
  refine this _,
  rw [mul_pow, mem_span_singleton],
  exact ⟨f x ^ (n + 1), mul_comm _ _⟩
end

end ideal

end comm_semiring

section ring

namespace ideal

variables [ring α] (I : ideal α) {a b : α}

lemma neg_mem_iff : -a ∈ I ↔ a ∈ I := I.neg_mem_iff

lemma add_mem_iff_left : b ∈ I → (a + b ∈ I ↔ a ∈ I) := I.add_mem_iff_left

lemma add_mem_iff_right : a ∈ I → (a + b ∈ I ↔ b ∈ I) := I.add_mem_iff_right

protected lemma sub_mem : a ∈ I → b ∈ I → a - b ∈ I := I.sub_mem

lemma mem_span_insert' {s : set α} {x y} :
  x ∈ span (insert y s) ↔ ∃a, x + a * y ∈ span s := submodule.mem_span_insert'

end ideal

end ring

section division_ring
variables {K : Type u} [division_ring K] (I : ideal K)

namespace ideal

/-- All ideals in a division ring are trivial. -/
lemma eq_bot_or_top : I = ⊥ ∨ I = ⊤ :=
begin
  rw or_iff_not_imp_right,
  change _ ≠ _ → _,
  rw ideal.ne_top_iff_one,
  intro h1,
  rw eq_bot_iff,
  intros r hr,
  by_cases H : r = 0, {simpa},
  simpa [H, h1] using I.mul_mem_left r⁻¹ hr,
end

lemma eq_bot_of_prime [h : I.is_prime] : I = ⊥ :=
or_iff_not_imp_right.mp I.eq_bot_or_top h.1

lemma bot_is_maximal : is_maximal (⊥ : ideal K) :=
⟨⟨λ h, absurd ((eq_top_iff_one (⊤ : ideal K)).mp rfl) (by rw ← h; simp),
λ I hI, or_iff_not_imp_left.mp (eq_bot_or_top I) (ne_of_gt hI)⟩⟩

end ideal

end division_ring

section comm_ring

namespace ideal

theorem mul_sub_mul_mem {R : Type*} [comm_ring R] (I : ideal R) {a b c d : R}
  (h1 : a - b ∈ I) (h2 : c - d ∈ I) : a * c - b * d ∈ I :=
begin
  rw (show a * c - b * d = (a - b) * c + b * (c - d), by {rw [sub_mul, mul_sub], abel}),
  exact I.add_mem (I.mul_mem_right _ h1) (I.mul_mem_left _ h2),
end

end ideal

end comm_ring

namespace ring

variables {R : Type*} [comm_ring R]

lemma not_is_field_of_subsingleton {R : Type*} [ring R] [subsingleton R] : ¬ is_field R :=
λ ⟨⟨x, y, hxy⟩, _, _⟩, hxy (subsingleton.elim x y)

lemma exists_not_is_unit_of_not_is_field [nontrivial R] (hf : ¬ is_field R) :
  ∃ x ≠ (0 : R), ¬ is_unit x :=
begin
  have : ¬ _ := λ h, hf ⟨exists_pair_ne R, mul_comm, h⟩,
  simp_rw is_unit_iff_exists_inv,
  push_neg at ⊢ this,
  obtain ⟨x, hx, not_unit⟩ := this,
  exact ⟨x, hx, not_unit⟩
end

lemma not_is_field_iff_exists_ideal_bot_lt_and_lt_top [nontrivial R] :
  ¬ is_field R ↔ ∃ I : ideal R, ⊥ < I ∧ I < ⊤ :=
begin
  split,
  { intro h,
    obtain ⟨x, nz, nu⟩ := exists_not_is_unit_of_not_is_field h,
    use ideal.span {x},
    rw [bot_lt_iff_ne_bot, lt_top_iff_ne_top],
    exact ⟨mt ideal.span_singleton_eq_bot.mp nz, mt ideal.span_singleton_eq_top.mp nu⟩ },
  { rintros ⟨I, bot_lt, lt_top⟩ hf,
    obtain ⟨x, mem, ne_zero⟩ := set_like.exists_of_lt bot_lt,
    rw submodule.mem_bot at ne_zero,
    obtain ⟨y, hy⟩ := hf.mul_inv_cancel ne_zero,
    rw [lt_top_iff_ne_top, ne.def, ideal.eq_top_iff_one, ← hy] at lt_top,
    exact lt_top (I.mul_mem_right _ mem), }
end

lemma not_is_field_iff_exists_prime [nontrivial R] :
  ¬ is_field R ↔ ∃ p : ideal R, p ≠ ⊥ ∧ p.is_prime :=
not_is_field_iff_exists_ideal_bot_lt_and_lt_top.trans
  ⟨λ ⟨I, bot_lt, lt_top⟩, let ⟨p, hp, le_p⟩ := I.exists_le_maximal (lt_top_iff_ne_top.mp lt_top) in
    ⟨p, bot_lt_iff_ne_bot.mp (lt_of_lt_of_le bot_lt le_p), hp.is_prime⟩,
   λ ⟨p, ne_bot, prime⟩, ⟨p, bot_lt_iff_ne_bot.mpr ne_bot, lt_top_iff_ne_top.mpr prime.1⟩⟩

/-- When a ring is not a field, the maximal ideals are nontrivial. -/
lemma ne_bot_of_is_maximal_of_not_is_field [nontrivial R] {M : ideal R} (max : M.is_maximal)
  (not_field : ¬ is_field R) : M ≠ ⊥ :=
begin
  rintros h,
  rw h at max,
  rcases max with ⟨⟨h1, h2⟩⟩,
  obtain ⟨I, hIbot, hItop⟩ := not_is_field_iff_exists_ideal_bot_lt_and_lt_top.mp not_field,
  exact ne_of_lt hItop (h2 I hIbot),
end

end ring

namespace ideal

/-- Maximal ideals in a non-field are nontrivial. -/
variables {R : Type u} [comm_ring R] [nontrivial R]
lemma bot_lt_of_maximal (M : ideal R) [hm : M.is_maximal] (non_field : ¬ is_field R) : ⊥ < M :=
begin
  rcases (ring.not_is_field_iff_exists_ideal_bot_lt_and_lt_top.1 non_field)
    with ⟨I, Ibot, Itop⟩,
  split, { simp },
  intro mle,
  apply @irrefl _ (<) _ (⊤ : ideal R),
  have : M = ⊥ := eq_bot_iff.mpr mle,
  rw this at *,
  rwa hm.1.2 I Ibot at Itop,
end

end ideal

variables {a b : α}

/-- The set of non-invertible elements of a monoid. -/
def nonunits (α : Type u) [monoid α] : set α := { a | ¬is_unit a }

@[simp] theorem mem_nonunits_iff [monoid α] : a ∈ nonunits α ↔ ¬ is_unit a := iff.rfl

theorem mul_mem_nonunits_right [comm_monoid α] :
  b ∈ nonunits α → a * b ∈ nonunits α :=
mt is_unit_of_mul_is_unit_right

theorem mul_mem_nonunits_left [comm_monoid α] :
  a ∈ nonunits α → a * b ∈ nonunits α :=
mt is_unit_of_mul_is_unit_left

theorem zero_mem_nonunits [semiring α] : 0 ∈ nonunits α ↔ (0:α) ≠ 1 :=
not_congr is_unit_zero_iff

@[simp] theorem one_not_mem_nonunits [monoid α] : (1:α) ∉ nonunits α :=
not_not_intro is_unit_one

theorem coe_subset_nonunits [semiring α] {I : ideal α} (h : I ≠ ⊤) :
  (I : set α) ⊆ nonunits α :=
λ x hx hu, h $ I.eq_top_of_is_unit_mem hx hu

lemma exists_max_ideal_of_mem_nonunits [comm_semiring α] (h : a ∈ nonunits α) :
  ∃ I : ideal α, I.is_maximal ∧ a ∈ I :=
begin
  have : ideal.span ({a} : set α) ≠ ⊤,
  { intro H, rw ideal.span_singleton_eq_top at H, contradiction },
  rcases ideal.exists_le_maximal _ this with ⟨I, Imax, H⟩,
  use [I, Imax], apply H, apply ideal.subset_span, exact set.mem_singleton a
end


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Abhimanyu Pallavi Sudhir, Jean Lo, Calle Sönne, Benjamin Davidson
-/
import analysis.special_functions.complex.arg
import analysis.special_functions.log.basic

/-!
# The complex `log` function

Basic properties, relationship with `exp`.
-/

noncomputable theory

namespace complex

open set filter

open_locale real topological_space

/-- Inverse of the `exp` function. Returns values such that `(log x).im > - π` and `(log x).im ≤ π`.
  `log 0 = 0`-/
@[pp_nodot] noncomputable def log (x : ℂ) : ℂ := x.abs.log + arg x * I

lemma log_re (x : ℂ) : x.log.re = x.abs.log := by simp [log]

lemma log_im (x : ℂ) : x.log.im = x.arg := by simp [log]

lemma neg_pi_lt_log_im (x : ℂ) : -π < (log x).im := by simp only [log_im, neg_pi_lt_arg]
lemma log_im_le_pi (x : ℂ) : (log x).im ≤ π := by simp only [log_im, arg_le_pi]

lemma exp_log {x : ℂ} (hx : x ≠ 0) : exp (log x) = x :=
by rw [log, exp_add_mul_I, ← of_real_sin, sin_arg, ← of_real_cos, cos_arg hx,
  ← of_real_exp, real.exp_log (abs_pos.2 hx), mul_add, of_real_div, of_real_div,
  mul_div_cancel' _ (of_real_ne_zero.2 (mt abs_eq_zero.1 hx)), ← mul_assoc,
  mul_div_cancel' _ (of_real_ne_zero.2 (mt abs_eq_zero.1 hx)), re_add_im]

@[simp] lemma range_exp : range exp = {0}ᶜ :=
set.ext $ λ x, ⟨by { rintro ⟨x, rfl⟩, exact exp_ne_zero x }, λ hx, ⟨log x, exp_log hx⟩⟩

lemma log_exp {x : ℂ} (hx₁ : -π < x.im) (hx₂: x.im ≤ π) : log (exp x) = x :=
by rw [log, abs_exp, real.log_exp, exp_eq_exp_re_mul_sin_add_cos, ← of_real_exp,
  arg_mul_cos_add_sin_mul_I (real.exp_pos _) ⟨hx₁, hx₂⟩, re_add_im]

lemma exp_inj_of_neg_pi_lt_of_le_pi {x y : ℂ} (hx₁ : -π < x.im) (hx₂ : x.im ≤ π)
  (hy₁ : - π < y.im) (hy₂ : y.im ≤ π) (hxy : exp x = exp y) : x = y :=
by rw [← log_exp hx₁ hx₂, ← log_exp hy₁ hy₂, hxy]

lemma of_real_log {x : ℝ} (hx : 0 ≤ x) : (x.log : ℂ) = log x :=
complex.ext
  (by rw [log_re, of_real_re, abs_of_nonneg hx])
  (by rw [of_real_im, log_im, arg_of_real_of_nonneg hx])

lemma log_of_real_re (x : ℝ) : (log (x : ℂ)).re = real.log x := by simp [log_re]

@[simp] lemma log_zero : log 0 = 0 := by simp [log]

@[simp] lemma log_one : log 1 = 0 := by simp [log]

lemma log_neg_one : log (-1) = π * I := by simp [log]

lemma log_I : log I = π / 2 * I := by simp [log]

lemma log_neg_I : log (-I) = -(π / 2) * I := by simp [log]

lemma two_pi_I_ne_zero : (2 * π * I : ℂ) ≠ 0 :=
by norm_num [real.pi_ne_zero, I_ne_zero]

lemma exp_eq_one_iff {x : ℂ} : exp x = 1 ↔ ∃ n : ℤ, x = n * ((2 * π) * I) :=
begin
  split,
  { intro h,
    rcases exists_unique_add_zsmul_mem_Ioc real.two_pi_pos x.im (-π) with ⟨n, hn, -⟩,
    use -n,
    rw [int.cast_neg, neg_mul, eq_neg_iff_add_eq_zero],
    have : (x + n * (2 * π * I)).im ∈ Ioc (-π) π, by simpa [two_mul, mul_add] using hn,
    rw [← log_exp this.1 this.2, exp_periodic.int_mul n, h, log_one] },
  { rintro ⟨n, rfl⟩, exact (exp_periodic.int_mul n).eq.trans exp_zero }
end

lemma exp_eq_exp_iff_exp_sub_eq_one {x y : ℂ} : exp x = exp y ↔ exp (x - y) = 1 :=
by rw [exp_sub, div_eq_one_iff_eq (exp_ne_zero _)]

lemma exp_eq_exp_iff_exists_int {x y : ℂ} : exp x = exp y ↔ ∃ n : ℤ, x = y + n * ((2 * π) * I) :=
by simp only [exp_eq_exp_iff_exp_sub_eq_one, exp_eq_one_iff, sub_eq_iff_eq_add']

@[simp] lemma countable_preimage_exp {s : set ℂ} : (exp ⁻¹' s).countable ↔ s.countable :=
begin
  refine ⟨λ hs, _, λ hs, _⟩,
  { refine ((hs.image exp).insert 0).mono _,
    rw [image_preimage_eq_inter_range, range_exp, ← diff_eq, ← union_singleton, diff_union_self],
    exact subset_union_left _ _ },
  { rw ← bUnion_preimage_singleton,
    refine hs.bUnion (λ z hz, _),
    rcases em (∃ w, exp w = z) with ⟨w, rfl⟩|hne,
    { simp only [preimage, mem_singleton_iff, exp_eq_exp_iff_exists_int, set_of_exists],
      exact countable_Union (λ m, countable_singleton _) },
    { push_neg at hne, simp [preimage, hne] } }
end

alias countable_preimage_exp ↔ _ _root_.set.countable.preimage_cexp

lemma tendsto_log_nhds_within_im_neg_of_re_neg_of_im_zero
  {z : ℂ} (hre : z.re < 0) (him : z.im = 0) :
  tendsto log (𝓝[{z : ℂ | z.im < 0}] z) (𝓝 $ real.log (abs z) - π * I) :=
begin
  have := (continuous_of_real.continuous_at.comp_continuous_within_at
    (continuous_abs.continuous_within_at.log _)).tendsto.add
    (((continuous_of_real.tendsto _).comp $
    tendsto_arg_nhds_within_im_neg_of_re_neg_of_im_zero hre him).mul tendsto_const_nhds),
  convert this,
  { simp [sub_eq_add_neg] },
  { lift z to ℝ using him, simpa using hre.ne }
end

lemma continuous_within_at_log_of_re_neg_of_im_zero
  {z : ℂ} (hre : z.re < 0) (him : z.im = 0) :
  continuous_within_at log {z : ℂ | 0 ≤ z.im} z :=
begin
  have := (continuous_of_real.continuous_at.comp_continuous_within_at
    (continuous_abs.continuous_within_at.log _)).tendsto.add
    ((continuous_of_real.continuous_at.comp_continuous_within_at $
    continuous_within_at_arg_of_re_neg_of_im_zero hre him).mul tendsto_const_nhds),
  convert this,
  { lift z to ℝ using him, simpa using hre.ne }
end

lemma tendsto_log_nhds_within_im_nonneg_of_re_neg_of_im_zero
  {z : ℂ} (hre : z.re < 0) (him : z.im = 0) :
  tendsto log (𝓝[{z : ℂ | 0 ≤ z.im}] z) (𝓝 $ real.log (abs z) + π * I) :=
by simpa only [log, arg_eq_pi_iff.2 ⟨hre, him⟩]
  using (continuous_within_at_log_of_re_neg_of_im_zero hre him).tendsto

@[simp] lemma map_exp_comap_re_at_bot : map exp (comap re at_bot) = 𝓝[≠] 0 :=
by rw [← comap_exp_nhds_zero, map_comap, range_exp, nhds_within]

@[simp] lemma map_exp_comap_re_at_top : map exp (comap re at_top) = comap abs at_top :=
begin
  rw [← comap_exp_comap_abs_at_top, map_comap, range_exp, inf_eq_left, le_principal_iff],
  exact eventually_ne_of_tendsto_norm_at_top tendsto_comap 0
end

end complex

section log_deriv

open complex filter
open_locale topological_space

variables {α : Type*}

lemma continuous_at_clog {x : ℂ} (h : 0 < x.re ∨ x.im ≠ 0) :
  continuous_at log x :=
begin
  refine continuous_at.add _ _,
  { refine continuous_of_real.continuous_at.comp _,
    refine (real.continuous_at_log _).comp complex.continuous_abs.continuous_at,
    rw abs_ne_zero,
    rintro rfl,
    simpa using h },
  { have h_cont_mul : continuous (λ x : ℂ, x * I), from continuous_id'.mul continuous_const,
    refine h_cont_mul.continuous_at.comp (continuous_of_real.continuous_at.comp _),
    exact continuous_at_arg h, },
end

lemma filter.tendsto.clog {l : filter α} {f : α → ℂ} {x : ℂ} (h : tendsto f l (𝓝 x))
  (hx : 0 < x.re ∨ x.im ≠ 0) :
  tendsto (λ t, log (f t)) l (𝓝 $ log x) :=
(continuous_at_clog hx).tendsto.comp h

variables [topological_space α]

lemma continuous_at.clog {f : α → ℂ} {x : α} (h₁ : continuous_at f x)
  (h₂ : 0 < (f x).re ∨ (f x).im ≠ 0) :
  continuous_at (λ t, log (f t)) x :=
h₁.clog h₂

lemma continuous_within_at.clog {f : α → ℂ} {s : set α} {x : α} (h₁ : continuous_within_at f s x)
  (h₂ : 0 < (f x).re ∨ (f x).im ≠ 0) :
  continuous_within_at (λ t, log (f t)) s x :=
h₁.clog h₂

lemma continuous_on.clog {f : α → ℂ} {s : set α} (h₁ : continuous_on f s)
  (h₂ : ∀ x ∈ s, 0 < (f x).re ∨ (f x).im ≠ 0) :
  continuous_on (λ t, log (f t)) s :=
λ x hx, (h₁ x hx).clog (h₂ x hx)

lemma continuous.clog {f : α → ℂ} (h₁ : continuous f) (h₂ : ∀ x, 0 < (f x).re ∨ (f x).im ≠ 0) :
  continuous (λ t, log (f t)) :=
continuous_iff_continuous_at.2 $ λ x, h₁.continuous_at.clog (h₂ x)

end log_deriv


/-
Copyright (c) 2022 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import data.nat.parity
import data.list.chain

/-!
# List of booleans

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

In this file we prove lemmas about the number of `ff`s and `tt`s in a list of booleans. First we
prove that the number of `ff`s plus the number of `tt` equals the length of the list. Then we prove
that in a list with alternating `tt`s and `ff`s, the number of `tt`s differs from the number of
`ff`s by at most one. We provide several versions of these statements.
-/

namespace list

@[simp]

@[simp]
theorem count_add_count_bnot (l : list bool) (b : bool) : count b l + count (!b) l = length l :=
by rw [add_comm, count_bnot_add_count]

@[simp] theorem count_ff_add_count_tt (l : list bool) : count ff l + count tt l = length l :=
count_bnot_add_count l tt

@[simp] theorem count_tt_add_count_ff (l : list bool) : count tt l + count ff l = length l :=
count_bnot_add_count l ff

lemma chain.count_bnot :
  ∀ {b : bool} {l : list bool}, chain (≠) b l → count (!b) l = count b l + length l % 2
| b [] h := rfl
| b (x :: l) h :=
  begin
    obtain rfl : b = !x := bool.eq_bnot_iff.2 (rel_of_chain_cons h),
    rw [bnot_bnot, count_cons_self, count_cons_of_ne x.bnot_ne_self,
      chain.count_bnot (chain_of_chain_cons h), length, add_assoc, nat.mod_two_add_succ_mod_two]
  end

namespace chain'

variables {l : list bool}

theorem count_bnot_eq_count (hl : chain' (≠) l) (h2 : even (length l)) (b : bool) :
  count (!b) l = count b l :=
begin
  cases l with x l, { refl },
  rw [length_cons, nat.even_add_one, nat.not_even_iff] at h2,
  suffices : count (!x) (x :: l) = count x (x :: l),
  { cases b; cases x; try { exact this }; exact this.symm },
  rw [count_cons_of_ne x.bnot_ne_self, hl.count_bnot, h2, count_cons_self]
end

theorem count_ff_eq_count_tt (hl : chain' (≠) l) (h2 : even (length l)) : count ff l = count tt l :=
hl.count_bnot_eq_count h2 tt

lemma count_bnot_le_count_add_one (hl : chain' (≠) l) (b : bool) :
  count (!b) l ≤ count b l + 1 :=
begin
  cases l with x l, { exact zero_le _ },
  obtain rfl | rfl : b = x ∨ b = !x, by simp only [bool.eq_bnot_iff, em],
  { rw [count_cons_of_ne b.bnot_ne_self, count_cons_self, hl.count_bnot, add_assoc],
    exact add_le_add_left (nat.mod_lt _ two_pos).le _ },
  { rw [bnot_bnot, count_cons_self, count_cons_of_ne x.bnot_ne_self, hl.count_bnot],
    exact add_le_add_right (le_add_right le_rfl) _ }
end

lemma count_ff_le_count_tt_add_one (hl : chain' (≠) l) : count ff l ≤ count tt l + 1 :=
hl.count_bnot_le_count_add_one tt

lemma count_tt_le_count_ff_add_one (hl : chain' (≠) l) : count tt l ≤ count ff l + 1 :=
hl.count_bnot_le_count_add_one ff

theorem two_mul_count_bool_of_even (hl : chain' (≠) l) (h2 : even (length l)) (b : bool) :
  2 * count b l = length l :=
by rw [← count_bnot_add_count l b, hl.count_bnot_eq_count h2, two_mul]

theorem two_mul_count_bool_eq_ite (hl : chain' (≠) l) (b : bool) :
  2 * count b l = if even (length l) then length l else
    if b ∈ l.head' then length l + 1 else length l - 1 :=
begin
  by_cases h2 : even (length l),
  { rw [if_pos h2, hl.two_mul_count_bool_of_even h2] },
  { cases l with x l, { exact (h2 even_zero).elim },
    simp only [if_neg h2, count_cons', mul_add, head', option.mem_some_iff, @eq_comm _ x],
    rw [length_cons, nat.even_add_one, not_not] at h2,
    replace hl : l.chain' (≠) := hl.tail,
    rw [hl.two_mul_count_bool_of_even h2],
    split_ifs; simp }
end

theorem length_sub_one_le_two_mul_count_bool (hl : chain' (≠) l) (b : bool) :
  length l - 1 ≤ 2 * count b l :=
by { rw [hl.two_mul_count_bool_eq_ite], split_ifs; simp [le_tsub_add, nat.le_succ_of_le] }

theorem length_div_two_le_count_bool (hl : chain' (≠) l) (b : bool) : length l / 2 ≤ count b l :=
begin
  rw [nat.div_le_iff_le_mul_add_pred two_pos, ← tsub_le_iff_right],
  exact length_sub_one_le_two_mul_count_bool hl b
end

lemma two_mul_count_bool_le_length_add_one (hl : chain' (≠) l) (b : bool) :
  2 * count b l ≤ length l + 1 :=
by { rw [hl.two_mul_count_bool_eq_ite], split_ifs; simp [nat.le_succ_of_le] }

end chain'

end list


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_462 :
  (1 / 2 + 1 / 3) * (1 / 2 - 1 / 3) = 5 / 36 :=
begin
  norm_num,
end


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

! This file was ported from Lean 3 source module linear_algebra.matrix.invariant_basis_number
! leanprover-community/mathlib commit 843240b048bbb19942c581fd64caecbbe96337be
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.LinearAlgebra.Matrix.ToLin
import Mathbin.LinearAlgebra.InvariantBasisNumber

/-!
# Invertible matrices over a ring with invariant basis number are square.
-/

variable {n m : Type _} [Fintype n] [DecidableEq n] [Fintype m] [DecidableEq m]

variable {R : Type _} [Semiring R] [InvariantBasisNumber R]

open Matrix

theorem Matrix.square_of_invertible (M : Matrix n m R) (N : Matrix m n R) (h : M ⬝ N = 1)
    (h' : N ⬝ M = 1) : Fintype.card n = Fintype.card m :=
  card_eq_of_linearEquiv R (Matrix.toLinearEquivRight'OfInv h' h)
#align matrix.square_of_invertible Matrix.square_of_invertible


import mynat.definition
import mynat.add

namespace mynat

theorem add_right_cancel (a t b : mynat) : a + t = b + t → a = b :=
begin [nat_num_game]
    induction t with n hd,
    {
        rw add_zero,
        rw add_zero,
        intro h,
        exact h,
    },
    {
        rw add_succ,
        rw add_succ,
        intro h,
        apply hd,
        apply succ_inj,
        exact h,
    },
end

end mynat