import incidence_world.level06 --hide
open IncidencePlane --hide

/-
# Incidence World

## Level 7: triangles, triangles...

We end this world by proving the existence of triangles using only incidence axioms. To solve this level, the mathematical proof in paper will be
provided to you. The goal is to divide it in two cases and reach a contradiction in each of them. In this sense, it is because we want to reach a contradiction
that we don't need to look for all the possible distributions in each of these two cases. As early as a contradiction is reached, we can prove the opposite of what
the last conclusion has shown us.

To give you some hints, remember these three Lean tips that might help you to step through the proof.

**Tip 1:** whenever a hypothesis looks like `h1 : P ∧ Q`, we can refer to `P` and `Q` as `h1.1` and `h1.2`, respectively.]

**Tip 2:** whenever you have a goal of the form `⊢ ∀ (P : Ω), ¬ (P ∧ Q ∧ R)`, the `intros` tactic wil make progress.

**Tip 3:** whenever we use the terms `r`, `s`, `t` to talk about lines in a mathematical proof in paper, we may want to use the terms of the
form `line_through A B` in Lean.

If needed, you can go back to the previous levels to remember how to use some tactics. Good luck! Let's do this!

## The mathematical proof in paper...

**Claim:** There exist three lines that do not have a point in common.

**Proof:** Let P be the point in common.

By the third axiom of incidence, let A, B and C be three non-collinear points that lie on the plane Ω.

By the first axiom of incidence, let `r` be the line that passes through the points A and B.

By the first axiom of incidence, let `s` be the line that passes through the points A and C.

By the first axiom of incidence, let `t` be the line that passes through the points B and C.

Let us assume that `r ≠ s`. By the lemma `ne_of_not_share_point`, since C ∈ s and C ∉ r, then the lines `r` and `s`
are not equal.

Now, we proceed with the proof by cases.

**Case 1:** Let P = A.

Let the point A be incident with the line `t`. Because A = P, then P ∈ t.

Let `s = t`. By the lemma `equal_lines_of_contain_two_points`, because A ≠ C, A ∈ s, A ∈ t, C ∈ s and C ∈ t, then `s = t`.

Let `s = r`. Because `s = t`, then `t = r`. By the lemma `equal_lines_of_contain_two_points`, because A ≠ B, A ∈ r, A ∈ t, B ∈ r and B ∈ t, then `t = r`.

But this is a contradiction because we previously proved that `r ≠ s`.

Therefore, it is not possible that three lines share a point in common, when P = A.

**Case 2:** Let P ≠ A.

Let `r = s`. By the lemma `equal_lines_of_contain_two_points`, because P ≠ A, P ∈ r, P ∈ s, A ∈ r and A ∈ s, then `r = s`.

But this is a contradiction because we previously proved that `r ≠ s`.

Therefore, it is not possible that three lines share a point in common, when P ≠ A.

Hence, we have shown that there exist three lines that do not have a point in common.

-/

/- Hint : Click here for a hint, in case you get stuck.
Remember that, when using a lemma, to prove the conclusion of the statement, you have to provide Lean with the previous hypotheses. To finish the proof,
the `tauto` tactic might help you. Try to write curly braces to structure the proof. Still bewildered? Click on "View source" (located on the top right
corner of the game screen) to see the solution.
-/

variables {Ω : Type} [IncidencePlane Ω] --hide

/- Lemma :
There exist three lines that do not have a point in common.
-/
lemma three_distinct_lines : ∃ (r s t: Line Ω), (∀ (P : Ω),
¬(P ∈ r ∧ P ∈ s ∧ P ∈ t)) :=
begin
  rcases existence Ω with ⟨A, B, C, ⟨hAB, hAC, hBC, h⟩⟩,
  use line_through A B,
  use line_through A C,
  use line_through B C,
  intros P H,
  have h1 : line_through A C ≠ line_through A B,
  {
    exact ne_of_not_share_point (line_through_right A C) h,
  },
  by_cases hPA : P = A,
  {
    have hAlBC : A ∈ line_through B C,
    {
      rw ← hPA,
      exact H.2.2,
    },
    have H1 : line_through A C = line_through B C,
    {
      exact equal_lines_of_contain_two_points hAC (line_through_left A C) hAlBC (line_through_right A C) (line_through_right B C),
    },
    have H2 : line_through A C = line_through A B,
    {
      rw H1,
      exact equal_lines_of_contain_two_points hAB hAlBC (line_through_left A B) (line_through_left B C) (line_through_right A B),
    },
    exact h1 H2,
  },
  {
    have h2 : line_through A C = line_through A B,
    {
      exact equal_lines_of_contain_two_points hPA H.2.1 H.1 (line_through_left A C) (line_through_left A B),
    },
    tauto,
  },

end


/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Eric Wieser
-/

import algebra.direct_sum.internal
import algebra.graded_monoid
import data.fintype.card
import data.mv_polynomial.variables

/-!
# Homogeneous polynomials

A multivariate polynomial `φ` is homogeneous of degree `n`
if all monomials occuring in `φ` have degree `n`.

## Main definitions/lemmas

* `is_homogeneous φ n`: a predicate that asserts that `φ` is homogeneous of degree `n`.
* `homogeneous_submodule σ R n`: the submodule of homogeneous polynomials of degree `n`.
* `homogeneous_component n`: the additive morphism that projects polynomials onto
  their summand that is homogeneous of degree `n`.
* `sum_homogeneous_component`: every polynomial is the sum of its homogeneous components

-/

open_locale big_operators

namespace mv_polynomial
variables {σ : Type*} {τ : Type*} {R : Type*} {S : Type*}

/-
TODO
* create definition for `∑ i in d.support, d i`
* show that `mv_polynomial σ R ≃ₐ[R] ⨁ i, homogeneous_submodule σ R i`
-/

/-- A multivariate polynomial `φ` is homogeneous of degree `n`
if all monomials occuring in `φ` have degree `n`. -/
def is_homogeneous [comm_semiring R] (φ : mv_polynomial σ R) (n : ℕ) :=
∀ ⦃d⦄, coeff d φ ≠ 0 → ∑ i in d.support, d i = n

variables (σ R)

/-- The submodule of homogeneous `mv_polynomial`s of degree `n`. -/
noncomputable def homogeneous_submodule [comm_semiring R] (n : ℕ) :
  submodule R (mv_polynomial σ R) :=
{ carrier := { x | x.is_homogeneous n },
  smul_mem' := λ r a ha c hc, begin
    rw coeff_smul at hc,
    apply ha,
    intro h,
    apply hc,
    rw h,
    exact smul_zero r,
  end,
  zero_mem' := λ d hd, false.elim (hd $ coeff_zero _),
  add_mem' := λ a b ha hb c hc, begin
    rw coeff_add at hc,
    obtain h|h : coeff c a ≠ 0 ∨ coeff c b ≠ 0,
    { contrapose! hc, simp only [hc, add_zero] },
    { exact ha h },
    { exact hb h }
  end }

variables {σ R}

@[simp] lemma mem_homogeneous_submodule [comm_semiring R] (n : ℕ) (p : mv_polynomial σ R) :
  p ∈ homogeneous_submodule σ R n ↔ p.is_homogeneous n := iff.rfl

variables (σ R)

/-- While equal, the former has a convenient definitional reduction. -/
lemma homogeneous_submodule_eq_finsupp_supported [comm_semiring R] (n : ℕ) :
  homogeneous_submodule σ R n = finsupp.supported _ R {d | ∑ i in d.support, d i = n} :=
begin
  ext,
  rw [finsupp.mem_supported, set.subset_def],
  simp only [finsupp.mem_support_iff, finset.mem_coe],
  refl,
end

variables {σ R}

lemma homogeneous_submodule_mul [comm_semiring R] (m n : ℕ) :
  homogeneous_submodule σ R m * homogeneous_submodule σ R n ≤ homogeneous_submodule σ R (m + n) :=
begin
  rw submodule.mul_le,
  intros φ hφ ψ hψ c hc,
  rw [coeff_mul] at hc,
  obtain ⟨⟨d, e⟩, hde, H⟩ := finset.exists_ne_zero_of_sum_ne_zero hc,
  have aux : coeff d φ ≠ 0 ∧ coeff e ψ ≠ 0,
  { contrapose! H,
    by_cases h : coeff d φ = 0;
    simp only [*, ne.def, not_false_iff, zero_mul, mul_zero] at * },
  specialize hφ aux.1, specialize hψ aux.2,
  rw finsupp.mem_antidiagonal at hde,
  classical,
  have hd' : d.support ⊆ d.support ∪ e.support := finset.subset_union_left _ _,
  have he' : e.support ⊆ d.support ∪ e.support := finset.subset_union_right _ _,
  rw [← hde, ← hφ, ← hψ, finset.sum_subset (finsupp.support_add),
    finset.sum_subset hd', finset.sum_subset he', ← finset.sum_add_distrib],
  { congr },
  all_goals { intros i hi, apply finsupp.not_mem_support_iff.mp },
end

section
variables [comm_semiring R]

variables {σ R}

lemma is_homogeneous_monomial (d : σ →₀ ℕ) (r : R) (n : ℕ) (hn : ∑ i in d.support, d i = n) :
  is_homogeneous (monomial d r) n :=
begin
  intros c hc,
  classical,
  rw coeff_monomial at hc,
  split_ifs at hc with h,
  { subst c, exact hn },
  { contradiction }
end
variables (σ) {R}

lemma is_homogeneous_C (r : R) :
  is_homogeneous (C r : mv_polynomial σ R) 0 :=
begin
  apply is_homogeneous_monomial,
  simp only [finsupp.zero_apply, finset.sum_const_zero],
end

variables (σ R)

lemma is_homogeneous_zero (n : ℕ) : is_homogeneous (0 : mv_polynomial σ R) n :=
(homogeneous_submodule σ R n).zero_mem

lemma is_homogeneous_one : is_homogeneous (1 : mv_polynomial σ R) 0 :=
is_homogeneous_C _ _

variables {σ} (R)

lemma is_homogeneous_X (i : σ) :
  is_homogeneous (X i : mv_polynomial σ R) 1 :=
begin
  apply is_homogeneous_monomial,
  simp only [finsupp.support_single_ne_zero one_ne_zero, finset.sum_singleton],
  exact finsupp.single_eq_same
end

end

namespace is_homogeneous
variables [comm_semiring R] {φ ψ : mv_polynomial σ R} {m n : ℕ}

lemma coeff_eq_zero (hφ : is_homogeneous φ n) (d : σ →₀ ℕ) (hd : ∑ i in d.support, d i ≠ n) :
  coeff d φ = 0 :=
by { have aux := mt (@hφ d) hd, classical, rwa not_not at aux }

lemma inj_right (hm : is_homogeneous φ m) (hn : is_homogeneous φ n) (hφ : φ ≠ 0) :
  m = n :=
begin
  obtain ⟨d, hd⟩ : ∃ d, coeff d φ ≠ 0 := exists_coeff_ne_zero hφ,
  rw [← hm hd, ← hn hd]
end

lemma add (hφ : is_homogeneous φ n) (hψ : is_homogeneous ψ n) :
  is_homogeneous (φ + ψ) n :=
(homogeneous_submodule σ R n).add_mem hφ hψ

lemma sum {ι : Type*} (s : finset ι) (φ : ι → mv_polynomial σ R) (n : ℕ)
  (h : ∀ i ∈ s, is_homogeneous (φ i) n) :
  is_homogeneous (∑ i in s, φ i) n :=
(homogeneous_submodule σ R n).sum_mem h

lemma mul (hφ : is_homogeneous φ m) (hψ : is_homogeneous ψ n) :
  is_homogeneous (φ * ψ) (m + n) :=
homogeneous_submodule_mul m n $ submodule.mul_mem_mul hφ hψ

lemma prod {ι : Type*} (s : finset ι) (φ : ι → mv_polynomial σ R) (n : ι → ℕ)
  (h : ∀ i ∈ s, is_homogeneous (φ i) (n i)) :
  is_homogeneous (∏ i in s, φ i) (∑ i in s, n i) :=
begin
  classical,
  revert h,
  apply finset.induction_on s,
  { intro, simp only [is_homogeneous_one, finset.sum_empty, finset.prod_empty] },
  { intros i s his IH h,
    simp only [his, finset.prod_insert, finset.sum_insert, not_false_iff],
    apply (h i (finset.mem_insert_self _ _)).mul (IH _),
    intros j hjs,
    exact h j (finset.mem_insert_of_mem hjs) }
end

lemma total_degree (hφ : is_homogeneous φ n) (h : φ ≠ 0) :
  total_degree φ = n :=
begin
  rw total_degree,
  apply le_antisymm,
  { apply finset.sup_le,
    intros d hd,
    rw mem_support_iff at hd,
    rw [finsupp.sum, hφ hd], },
  { obtain ⟨d, hd⟩ : ∃ d, coeff d φ ≠ 0 := exists_coeff_ne_zero h,
    simp only [← hφ hd, finsupp.sum],
    replace hd := finsupp.mem_support_iff.mpr hd,
    exact finset.le_sup hd, }
end

/--
The homogeneous submodules form a graded ring. This instance is used by `direct_sum.comm_semiring`
and `direct_sum.algebra`. -/
instance homogeneous_submodule.gcomm_semiring :
  set_like.graded_monoid (homogeneous_submodule σ R) :=
{ one_mem := is_homogeneous_one σ R,
  mul_mem := λ i j xi xj, is_homogeneous.mul}

open_locale direct_sum
noncomputable example : comm_semiring (⨁ i, homogeneous_submodule σ R i) := infer_instance
noncomputable example : algebra R (⨁ i, homogeneous_submodule σ R i) := infer_instance

end is_homogeneous

section
noncomputable theory
open_locale classical
open finset

/-- `homogeneous_component n φ` is the part of `φ` that is homogeneous of degree `n`.
See `sum_homogeneous_component` for the statement that `φ` is equal to the sum
of all its homogeneous components. -/
def homogeneous_component [comm_semiring R] (n : ℕ) :
  mv_polynomial σ R →ₗ[R] mv_polynomial σ R :=
(submodule.subtype _).comp $ finsupp.restrict_dom _ _ {d | ∑ i in d.support, d i = n}

section homogeneous_component
open finset
variables [comm_semiring R] (n : ℕ) (φ : mv_polynomial σ R)

lemma coeff_homogeneous_component (d : σ →₀ ℕ) :
  coeff d (homogeneous_component n φ) = if ∑ i in d.support, d i = n then coeff d φ else 0 :=
by convert finsupp.filter_apply (λ d : σ →₀ ℕ, ∑ i in d.support, d i = n) φ d

lemma homogeneous_component_apply :
  homogeneous_component n φ =
  ∑ d in φ.support.filter (λ d, ∑ i in d.support, d i = n), monomial d (coeff d φ) :=
by convert finsupp.filter_eq_sum (λ d : σ →₀ ℕ, ∑ i in d.support, d i = n) φ

lemma homogeneous_component_is_homogeneous :
  (homogeneous_component n φ).is_homogeneous n :=
begin
  intros d hd,
  contrapose! hd,
  rw [coeff_homogeneous_component, if_neg hd]
end

lemma homogeneous_component_zero : homogeneous_component 0 φ = C (coeff 0 φ) :=
begin
  ext1 d,
  rcases em (d = 0) with (rfl|hd),
  { simp only [coeff_homogeneous_component, sum_eq_zero_iff, finsupp.zero_apply, if_true, coeff_C,
      eq_self_iff_true, forall_true_iff] },
  { rw [coeff_homogeneous_component, if_neg, coeff_C, if_neg (ne.symm hd)],
    simp only [finsupp.ext_iff, finsupp.zero_apply] at hd,
    simp [hd] }
end

lemma homogeneous_component_eq_zero' (h : ∀ d : σ →₀ ℕ, d ∈ φ.support → ∑ i in d.support, d i ≠ n) :
  homogeneous_component n φ = 0 :=
begin
  rw [homogeneous_component_apply, sum_eq_zero],
  intros d hd, rw mem_filter at hd,
  exfalso, exact h _ hd.1 hd.2
end

lemma homogeneous_component_eq_zero (h : φ.total_degree < n) :
  homogeneous_component n φ = 0 :=
begin
  apply homogeneous_component_eq_zero',
  rw [total_degree, finset.sup_lt_iff] at h,
  { intros d hd, exact ne_of_lt (h d hd), },
  { exact lt_of_le_of_lt (nat.zero_le _) h, }
end

lemma sum_homogeneous_component :
  ∑ i in range (φ.total_degree + 1), homogeneous_component i φ = φ :=
begin
  ext1 d,
  suffices : φ.total_degree < d.support.sum d → 0 = coeff d φ,
    by simpa [coeff_sum, coeff_homogeneous_component],
  exact λ h, (coeff_eq_zero_of_total_degree_lt h).symm
end

end homogeneous_component

end

end mv_polynomial


import analysis.special_functions.pow

/-!
Sean a, b y c tres números reales y positivos. Demostrar que
\frac{a}{\sqrt{a^2 + 8bc}} +
\frac{b}{\sqrt{b^2 + 8ca}} +
\frac{c}{\sqrt{c^2 + 8ab}} ≥ 1.
-/

open real

variables {a b c : ℝ}

lemma suma_pos
  (ha : 0 < a)
  (hb : 0 < b)
  (hc : 0 < c)
  : 0 < a ^ 4 + b ^ 4 + c ^ 4 :=
begin
  have ha1 : 0 < a ^ 4          := pow_pos ha 4,
  have hb1 : 0 < b ^ 4          := pow_pos hb 4,
  have hc1 : 0 < c ^ 4          := pow_pos hc 4,
  have sum1 : 0 < a ^ 4 + b ^ 4 := add_pos ha1 hb1,
  exact (add_pos sum1 hc1),
end

-- Una demostración alternativa del lema anterior es la siguiente:
example
  (ha : 0 < a)
  (hb : 0 < b)
  (hc : 0 < c)
  : 0 < a ^ 4 + b ^ 4 + c ^ 4 :=
begin
  apply add_pos,
  { apply add_pos,
    { exact pow_pos ha 4 },
    { exact pow_pos hb 4 }},
  { exact pow_pos hc 4 },
end

lemma cota
  (ha : 0 < a)
  (hb : 0 < b)
  (hc : 0 < c)
  : a ^ 4 / (a ^ 4 + b ^ 4 + c ^ 4) ≤
    a ^ 3 / sqrt ((a ^ 3) ^ 2 + 8 * b ^ 3 * c ^ 3) :=
begin
  have ha3 : 0 < a ^ 3 :=
    pow_pos ha 3,
  have ha32 : 0 ≤ (a ^ 3) ^ 2 :=
    pow_two_nonneg (a ^ 3),
  have hb8 : 0 < 8 * b ^ 3 :=
    mul_pos (by norm_num) (pow_pos hb 3),
  have hbc : 0 < 8 * b ^ 3 * c ^ 3 :=
    mul_pos hb8 (pow_pos hc 3),
  have hdenom1 : 0 < (a ^ 3) ^ 2 + 8 * b ^ 3 * c ^ 3 :=
    add_pos_of_nonneg_of_pos ha32 hbc,
  have hsqrt : 0 < sqrt ((a ^ 3) ^ 2 + 8 * b ^ 3 * c ^ 3) :=
    sqrt_pos.mpr hdenom1,
  have hdenom2 : 0 < a ^ 4 + b ^ 4 + c ^ 4 :=
    suma_pos ha hb hc,
  have hdenom3 : 0 ≤ a ^ 4 + b ^ 4 + c ^ 4 :=
    le_of_lt hdenom2,
  rw div_le_div_iff hdenom2 hsqrt,
  rw pow_succ',
  rw mul_assoc,
  apply mul_le_mul_of_nonneg_left _ (le_of_lt ha3),
  rw ← pow_succ',
  apply le_of_pow_le_pow _ hdenom3 zero_lt_two,
  rw mul_pow,
  rw sq_sqrt (le_of_lt hdenom1),
  rw ← sub_nonneg,
  have desarrollo :
    (a ^ 4 + b ^ 4 + c ^ 4) ^ 2 -
       a ^ 2 * ((a ^ 3) ^ 2 + 8 * b ^ 3 * c ^ 3)
    = 2 * (a ^ 2 * (b ^ 2 - c ^ 2)) ^ 2 + (b ^ 4 - c ^ 4) ^ 2 +
      (2 * (a ^ 2 * b * c - b ^ 2 * c ^ 2)) ^ 2 :=
    by ring,
  have h1 : 0 ≤ (a ^ 2 * (b ^ 2 - c ^ 2)) ^ 2 :=
    pow_two_nonneg _,
  have h1' : 0 ≤ 2 * (a ^ 2 * (b ^ 2 - c ^ 2)) ^ 2:=
    mul_nonneg zero_le_two h1,
  have h2 : 0 ≤ (b ^ 4 - c ^ 4) ^ 2:=
    pow_two_nonneg _,
  have h3 : 0 ≤ (2 * (a ^ 2 * b * c - b ^ 2 * c ^ 2)) ^ 2 :=
    pow_two_nonneg _,
  have aux1 : 0 ≤ (b ^ 4 - c ^ 4) ^ 2 +
                  (2 * (a ^ 2 * b * c - b ^ 2 * c ^ 2)) ^ 2 :=
    add_nonneg h2 h3,
  have tesis : 0 ≤ 2 * (a ^ 2 * (b ^ 2 - c ^ 2)) ^ 2 +
                   ((b ^ 4 - c ^ 4) ^ 2 +
                    (2 * (a ^ 2 * b * c - b ^ 2 * c ^ 2)) ^ 2) :=
    add_nonneg h1' aux1,
  rw desarrollo,
  rw add_assoc,
  exact tesis,
end

theorem imo2001_q2_aux
  (ha : 0 < a)
  (hb : 0 < b)
  (hc : 0 < c)
  : 1 ≤ a ^ 3 / sqrt ((a ^ 3) ^ 2 + 8 * b ^ 3 * c ^ 3) +
        b ^ 3 / sqrt ((b ^ 3) ^ 2 + 8 * c ^ 3 * a ^ 3) +
        c ^ 3 / sqrt ((c ^ 3) ^ 2 + 8 * a ^ 3 * b ^ 3) :=
begin
  have h1 : b ^ 4 + c ^ 4 + a ^ 4 = a ^ 4 + b ^ 4 + c ^ 4,
    by ring,
  have h2 : c ^ 4 + a ^ 4 + b ^ 4 = a ^4 + b ^ 4 + c ^ 4,
    by ring,
  have h3 : a ^ 4 / (a ^ 4 + b ^ 4 + c ^ 4) +
            b ^ 4 / (b ^ 4 + c ^ 4 + a ^ 4) ≤
            a ^ 3 / sqrt ((a ^ 3) ^ 2 + 8 * b ^ 3 * c ^ 3) +
            b ^ 3 / sqrt ((b ^ 3) ^ 2 + 8 * c ^ 3 * a ^ 3) :=
    add_le_add (cota ha hb hc) (cota hb hc ha),
  have h4 : a ^ 4 / (a ^ 4 + b ^ 4 + c ^ 4) +
            b ^ 4 / (b ^ 4 + c ^ 4 + a ^ 4) +
            c ^ 4 / (c ^ 4 + a ^ 4 + b ^ 4) ≤
            a ^ 3 / sqrt ((a ^ 3) ^ 2 + 8 * b ^ 3 * c ^ 3) +
            b ^ 3 / sqrt ((b ^ 3) ^ 2 + 8 * c ^ 3 * a ^ 3) +
            c ^ 3 / sqrt ((c ^ 3) ^ 2 + 8 * a ^ 3 * b ^ 3) :=
    add_le_add h3 (cota hc ha hb),
  have igualdad : a ^ 4 / (a ^ 4 + b ^ 4 + c ^ 4) +
                  b ^ 4 / (b ^ 4 + c ^ 4 + a ^ 4) +
                  c ^ 4 / (c ^ 4 + a ^ 4 + b ^ 4) = 1,
    { rw h1,
      rw h2,
      rw ← add_div,
      rw ← add_div,
      rw div_self,
      apply ne_of_gt,
      exact suma_pos ha hb hc,},
  rw igualdad at h4,
  exact h4,
end

theorem imo2001_q2
  (ha : 0 < a)
  (hb : 0 < b)
  (hc : 0 < c)
  : 1 ≤ a / sqrt (a ^ 2 + 8 * b * c) +
        b / sqrt (b ^ 2 + 8 * c * a) +
        c / sqrt (c ^ 2 + 8 * a * b) :=
begin
  have h : ∀ {x : ℝ}, 0 < x → (x ^ (3 : ℝ)⁻¹) ^ 3 = x,
    { intros x hx,
      have h1 : 0 < 3 :=
        zero_lt_three,
      have h2 : ↑3 = (3 : ℝ) :=
        by norm_num,
      have htesis : (x ^ (↑3)⁻¹) ^ 3 = x :=
        rpow_nat_inv_pow_nat (le_of_lt hx) h1,
      rw h2 at htesis,
      exact htesis,},
  have aux :=
    imo2001_q2_aux (rpow_pos_of_pos ha 3⁻¹)
                   (rpow_pos_of_pos hb 3⁻¹)
                   (rpow_pos_of_pos hc 3⁻¹),
  rw h ha at aux,
  rw h hb at aux,
  rw h hc at aux,
  exact aux,
end


/-
Copyright (c) 2021 Kalle Kytölä. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kalle Kytölä, Yury Kudryashov

! This file was ported from Lean 3 source module analysis.normed_space.weak_dual
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Algebra.Module.WeakDual
import Mathbin.Analysis.NormedSpace.Dual
import Mathbin.Analysis.NormedSpace.OperatorNorm

/-!
# Weak dual of normed space

Let `E` be a normed space over a field `𝕜`. This file is concerned with properties of the weak-*
topology on the dual of `E`. By the dual, we mean either of the type synonyms
`normed_space.dual 𝕜 E` or `weak_dual 𝕜 E`, depending on whether it is viewed as equipped with its
usual operator norm topology or the weak-* topology.

It is shown that the canonical mapping `normed_space.dual 𝕜 E → weak_dual 𝕜 E` is continuous, and
as a consequence the weak-* topology is coarser than the topology obtained from the operator norm
(dual norm).

In this file, we also establish the Banach-Alaoglu theorem about the compactness of closed balls
in the dual of `E` (as well as sets of somewhat more general form) with respect to the weak-*
topology.

## Main definitions

The main definitions concern the canonical mapping `dual 𝕜 E → weak_dual 𝕜 E`.

* `normed_space.dual.to_weak_dual` and `weak_dual.to_normed_dual`: Linear equivalences from
  `dual 𝕜 E` to `weak_dual 𝕜 E` and in the converse direction.
* `normed_space.dual.continuous_linear_map_to_weak_dual`: A continuous linear mapping from
  `dual 𝕜 E` to `weak_dual 𝕜 E` (same as `normed_space.dual.to_weak_dual` but different bundled
  data).

## Main results

The first main result concerns the comparison of the operator norm topology on `dual 𝕜 E` and the
weak-* topology on (its type synonym) `weak_dual 𝕜 E`:
* `dual_norm_topology_le_weak_dual_topology`: The weak-* topology on the dual of a normed space is
  coarser (not necessarily strictly) than the operator norm topology.
* `weak_dual.is_compact_polar` (a version of the Banach-Alaoglu theorem): The polar set of a
  neighborhood of the origin in a normed space `E` over `𝕜` is compact in `weak_dual _ E`, if the
  nontrivially normed field `𝕜` is proper as a topological space.
* `weak_dual.is_compact_closed_ball` (the most common special case of the Banach-Alaoglu theorem):
  Closed balls in the dual of a normed space `E` over `ℝ` or `ℂ` are compact in the weak-star
  topology.

TODOs:
* Add that in finite dimensions, the weak-* topology and the dual norm topology coincide.
* Add that in infinite dimensions, the weak-* topology is strictly coarser than the dual norm
  topology.
* Add metrizability of the dual unit ball (more generally weak-star compact subsets) of
  `weak_dual 𝕜 E` under the assumption of separability of `E`.
* Add the sequential Banach-Alaoglu theorem: the dual unit ball of a separable normed space `E`
  is sequentially compact in the weak-star topology. This would follow from the metrizability above.

## Notations

No new notation is introduced.

## Implementation notes

Weak-* topology is defined generally in the file `topology.algebra.module.weak_dual`.

When `E` is a normed space, the duals `dual 𝕜 E` and `weak_dual 𝕜 E` are type synonyms with
different topology instances.

For the proof of Banach-Alaoglu theorem, the weak dual of `E` is embedded in the space of
functions `E → 𝕜` with the topology of pointwise convergence.

The polar set `polar 𝕜 s` of a subset `s` of `E` is originally defined as a subset of the dual
`dual 𝕜 E`. We care about properties of these w.r.t. weak-* topology, and for this purpose give
the definition `weak_dual.polar 𝕜 s` for the "same" subset viewed as a subset of `weak_dual 𝕜 E`
(a type synonym of the dual but with a different topology instance).

## References

* https://en.wikipedia.org/wiki/Weak_topology#Weak-*_topology
* https://en.wikipedia.org/wiki/Banach%E2%80%93Alaoglu_theorem

## Tags

weak-star, weak dual

-/

noncomputable section

open Filter Function Metric Set

open Topology Filter

/-!
### Weak star topology on duals of normed spaces

In this section, we prove properties about the weak-* topology on duals of normed spaces.
We prove in particular that the canonical mapping `dual 𝕜 E → weak_dual 𝕜 E` is continuous,
i.e., that the weak-* topology is coarser (not necessarily strictly) than the topology given
by the dual-norm (i.e. the operator-norm).
-/

variable {𝕜 : Type _} [NontriviallyNormedField 𝕜]

variable {E : Type _} [SeminormedAddCommGroup E] [NormedSpace 𝕜 E]

namespace NormedSpace

namespace Dual

/-- For normed spaces `E`, there is a canonical map `dual 𝕜 E → weak_dual 𝕜 E` (the "identity"
mapping). It is a linear equivalence. -/
def toWeakDual : Dual 𝕜 E ≃ₗ[𝕜] WeakDual 𝕜 E :=
  LinearEquiv.refl 𝕜 (E →L[𝕜] 𝕜)
#align normed_space.dual.to_weak_dual NormedSpace.Dual.toWeakDual

@[simp]
theorem coe_toWeakDual (x' : Dual 𝕜 E) : ⇑x'.toWeakDual = x' :=
  rfl
#align normed_space.dual.coe_to_weak_dual NormedSpace.Dual.coe_toWeakDual

@[simp]
theorem toWeakDual_eq_iff (x' y' : Dual 𝕜 E) : x'.toWeakDual = y'.toWeakDual ↔ x' = y' :=
  toWeakDual.Injective.eq_iff
#align normed_space.dual.to_weak_dual_eq_iff NormedSpace.Dual.toWeakDual_eq_iff

theorem toWeakDual_continuous : Continuous fun x' : Dual 𝕜 E => x'.toWeakDual :=
  WeakBilin.continuous_of_continuous_eval _ fun z => (inclusionInDoubleDual 𝕜 E z).Continuous
#align normed_space.dual.to_weak_dual_continuous NormedSpace.Dual.toWeakDual_continuous

/-- For a normed space `E`, according to `to_weak_dual_continuous` the "identity mapping"
`dual 𝕜 E → weak_dual 𝕜 E` is continuous. This definition implements it as a continuous linear
map. -/
def continuousLinearMapToWeakDual : Dual 𝕜 E →L[𝕜] WeakDual 𝕜 E :=
  { toWeakDual with cont := toWeakDual_continuous }
#align normed_space.dual.continuous_linear_map_to_weak_dual NormedSpace.Dual.continuousLinearMapToWeakDual

/-- The weak-star topology is coarser than the dual-norm topology. -/
theorem dual_norm_topology_le_weak_dual_topology :
    (by infer_instance : TopologicalSpace (Dual 𝕜 E)) ≤
      (by infer_instance : TopologicalSpace (WeakDual 𝕜 E)) :=
  by
  convert to_weak_dual_continuous.le_induced
  exact induced_id.symm
#align normed_space.dual.dual_norm_topology_le_weak_dual_topology NormedSpace.Dual.dual_norm_topology_le_weak_dual_topology

end Dual

end NormedSpace

namespace WeakDual

open NormedSpace

/-- For normed spaces `E`, there is a canonical map `weak_dual 𝕜 E → dual 𝕜 E` (the "identity"
mapping). It is a linear equivalence. Here it is implemented as the inverse of the linear
equivalence `normed_space.dual.to_weak_dual` in the other direction. -/
def toNormedDual : WeakDual 𝕜 E ≃ₗ[𝕜] Dual 𝕜 E :=
  NormedSpace.Dual.toWeakDual.symm
#align weak_dual.to_normed_dual WeakDual.toNormedDual

theorem toNormedDual_apply (x : WeakDual 𝕜 E) (y : E) : (toNormedDual x) y = x y :=
  rfl
#align weak_dual.to_normed_dual_apply WeakDual.toNormedDual_apply

@[simp]
theorem coe_toNormedDual (x' : WeakDual 𝕜 E) : ⇑x'.toNormedDual = x' :=
  rfl
#align weak_dual.coe_to_normed_dual WeakDual.coe_toNormedDual

@[simp]
theorem toNormedDual_eq_iff (x' y' : WeakDual 𝕜 E) : x'.toNormedDual = y'.toNormedDual ↔ x' = y' :=
  WeakDual.toNormedDual.Injective.eq_iff
#align weak_dual.to_normed_dual_eq_iff WeakDual.toNormedDual_eq_iff

theorem isClosed_closedBall (x' : Dual 𝕜 E) (r : ℝ) : IsClosed (toNormedDual ⁻¹' closedBall x' r) :=
  isClosed_induced_iff'.2 (ContinuousLinearMap.is_weak_closed_closedBall x' r)
#align weak_dual.is_closed_closed_ball WeakDual.isClosed_closedBall

/-!
### Polar sets in the weak dual space
-/

variable (𝕜)

/-- The polar set `polar 𝕜 s` of `s : set E` seen as a subset of the dual of `E` with the
weak-star topology is `weak_dual.polar 𝕜 s`. -/
def polar (s : Set E) : Set (WeakDual 𝕜 E) :=
  toNormedDual ⁻¹' polar 𝕜 s
#align weak_dual.polar WeakDual.polar

theorem polar_def (s : Set E) : polar 𝕜 s = { f : WeakDual 𝕜 E | ∀ x ∈ s, ‖f x‖ ≤ 1 } :=
  rfl
#align weak_dual.polar_def WeakDual.polar_def

/-- The polar `polar 𝕜 s` of a set `s : E` is a closed subset when the weak star topology
is used. -/
theorem isClosed_polar (s : Set E) : IsClosed (polar 𝕜 s) :=
  by
  simp only [polar_def, set_of_forall]
  exact isClosed_binterᵢ fun x hx => is_closed_Iic.preimage (WeakBilin.eval_continuous _ _).norm
#align weak_dual.is_closed_polar WeakDual.isClosed_polar

variable {𝕜}

/-- While the coercion `coe_fn : weak_dual 𝕜 E → (E → 𝕜)` is not a closed map, it sends *bounded*
closed sets to closed sets. -/
theorem isClosed_image_coe_of_bounded_of_closed {s : Set (WeakDual 𝕜 E)}
    (hb : Bounded (Dual.toWeakDual ⁻¹' s)) (hc : IsClosed s) :
    IsClosed ((coeFn : WeakDual 𝕜 E → E → 𝕜) '' s) :=
  ContinuousLinearMap.isClosed_image_coe_of_bounded_of_weak_closed hb (isClosed_induced_iff'.1 hc)
#align weak_dual.is_closed_image_coe_of_bounded_of_closed WeakDual.isClosed_image_coe_of_bounded_of_closed

theorem isCompact_of_bounded_of_closed [ProperSpace 𝕜] {s : Set (WeakDual 𝕜 E)}
    (hb : Bounded (Dual.toWeakDual ⁻¹' s)) (hc : IsClosed s) : IsCompact s :=
  (Embedding.isCompact_iff_isCompact_image FunLike.coe_injective.embedding_induced).mpr <|
    ContinuousLinearMap.isCompact_image_coe_of_bounded_of_closed_image hb <|
      isClosed_image_coe_of_bounded_of_closed hb hc
#align weak_dual.is_compact_of_bounded_of_closed WeakDual.isCompact_of_bounded_of_closed

variable (𝕜)

/-- The image under `coe_fn : weak_dual 𝕜 E → (E → 𝕜)` of a polar `weak_dual.polar 𝕜 s` of a
neighborhood `s` of the origin is a closed set. -/
theorem isClosed_image_polar_of_mem_nhds {s : Set E} (s_nhd : s ∈ 𝓝 (0 : E)) :
    IsClosed ((coeFn : WeakDual 𝕜 E → E → 𝕜) '' polar 𝕜 s) :=
  isClosed_image_coe_of_bounded_of_closed (bounded_polar_of_mem_nhds_zero 𝕜 s_nhd)
    (isClosed_polar _ _)
#align weak_dual.is_closed_image_polar_of_mem_nhds WeakDual.isClosed_image_polar_of_mem_nhds

/-- The image under `coe_fn : normed_space.dual 𝕜 E → (E → 𝕜)` of a polar `polar 𝕜 s` of a
neighborhood `s` of the origin is a closed set. -/
theorem NormedSpace.Dual.isClosed_image_polar_of_mem_nhds {s : Set E} (s_nhd : s ∈ 𝓝 (0 : E)) :
    IsClosed ((coeFn : Dual 𝕜 E → E → 𝕜) '' NormedSpace.polar 𝕜 s) :=
  isClosed_image_polar_of_mem_nhds 𝕜 s_nhd
#align normed_space.dual.is_closed_image_polar_of_mem_nhds NormedSpace.Dual.isClosed_image_polar_of_mem_nhds

/-- The **Banach-Alaoglu theorem**: the polar set of a neighborhood `s` of the origin in a
normed space `E` is a compact subset of `weak_dual 𝕜 E`. -/
theorem isCompact_polar [ProperSpace 𝕜] {s : Set E} (s_nhd : s ∈ 𝓝 (0 : E)) :
    IsCompact (polar 𝕜 s) :=
  isCompact_of_bounded_of_closed (bounded_polar_of_mem_nhds_zero 𝕜 s_nhd) (isClosed_polar _ _)
#align weak_dual.is_compact_polar WeakDual.isCompact_polar

/-- The **Banach-Alaoglu theorem**: closed balls of the dual of a normed space `E` are compact in
the weak-star topology. -/
theorem isCompact_closedBall [ProperSpace 𝕜] (x' : Dual 𝕜 E) (r : ℝ) :
    IsCompact (toNormedDual ⁻¹' closedBall x' r) :=
  isCompact_of_bounded_of_closed bounded_closedBall (isClosed_closedBall x' r)
#align weak_dual.is_compact_closed_ball WeakDual.isCompact_closedBall

end WeakDual


import UnitConjecture.GardamGroup
import Mathlib.Algebra.GroupPower.Basic

/-!
## Torsion-freeness of `P`

This file contains a proof that the group `P` defined is in fact torsion-free.

Roughly, the steps are as follows (further details can be found in the corresponding `.md` file):
1. Define a function `sq : P -> K` taking a group element `(q, k)` to its square. This element lies in the kernel as the group `ℤ/2 × ℤ/2` has exponent `2`.
2. Show that elements in `K`, which are integer triples of the form `(a, b, c)`, do not have torsion. This requires the fact that the group `ℤ`, and hence `ℤ³`, is torsion-free.
3. Show that no element of `P` has order precisely `2`. This is an argument by cases on the `Q` part of a general element of `P`.
4. Finally, show that if an element `g : G` of a group `G` satisfies `g ^ n = (1 : G)`, then it also satisfies `(g ^ 2) ^ n = (1 : G)`.
5. Together, these statements show that `P` is torsion-free.
-/

section TorsionFree

/-!
### Torsion-free groups
-/

/-- The definition of a torsion-free group. -/
class TorsionFree (G : Type _) [Group G] where
  /-- A group is *torsion-free* if the only element with non-trivial torsion is the identity element. -/
  torsionFree : ∀ g : G, ∀ n : ℕ, g ^ n.succ = 1 → g = 1

/-- The definition of torsion-free additive groups. -/
class AddTorsionFree (A : Type _) [AddGroup A] where
  /-- An additive group is *torsion-free* if the only element with non-trivial torsion is the identity element. -/
  torsionFree : ∀ a : A, ∀ n : ℕ, n.succ • a = 0 → a = 0

/-- ℤ is torsion-free, since it is an integral domain. -/
instance : AddTorsionFree ℤ where
  torsionFree := by
    intro a n (h : n.succ * a = 0)
    cases Int.mul_eq_zero.mp h with
    | inl hyp => injection hyp; contradiction
    | inr _ => assumption

/-- The product of torsion-free additive groups is torsion-free. -/
instance {A B : Type _} [AddGroup A] [AddGroup B] [AddTorsionFree A] [AddTorsionFree B] : AddTorsionFree (A × B) where
  torsionFree := by
    intro (a, b) n
    rw [show n.succ • (a, b) = (n.succ • a, n.succ • b) from rfl, Prod.ext_iff, Prod.ext_iff]
    intro ⟨_, _⟩; refine' ⟨_, _⟩ <;>
      (apply AddTorsionFree.torsionFree; assumption)

end TorsionFree

/-! ### **Step 1:** Defining the square of an element of `P`. -/

/-- The function taking an element of `P` to its square, which lies in the kernel `K`. -/
@[aesop norm unfold (rule_sets [P]), reducible]
def P.sq : P → K
  | ((p, q, r), .e) => (p + p, q + q, r + r)
  | ((_, q, _), .b) => (0, q + q + 1, 0)
  | ((p, _, _), .a) => (p + p + 1, 0, 0)
  | ((_, _, r), .c) => (0, 0, r + r + 1)

open P

/-- A proof that the function `sq` indeed takes an element of `P` to its square in `K`. -/
@[aesop norm apply (rule_sets [P]), simp]
theorem sq_square : ∀ g : P, g * g = (P.sq g, .e)
  | ((p, q, r), x) =>
    match x with
    | .e | .a | .b | .c => by
      aesop (rule_sets [P])

/-! ### **Step 2:** Proving that `K` (= `ℤ³`) is torsion-free. -/

/-- The kernel `ℤ³` is torsion-free. -/
instance K.torsionFree : AddTorsionFree K := inferInstance

/-! ### **Step 3:** Showing that no element of `P` has order precisely two. -/

namespace Int

/-! Some basic lemmas about integers needed to prove facts about `P`. -/

lemma add_twice_eq_mul_two {a : ℤ} : a + a = a * 2 := by
  rw [show 2 = 1 + 1 by rfl, mul_add, mul_one]

attribute [local simp] add_twice_eq_mul_two

/-- No odd integer is zero. -/
lemma odd_ne_zero : ∀ a : ℤ, ¬(a + a + 1 = 0) := by
  intro a h
  have : (a + a + 1) % 2 = 0 % 2 := congrArg (· % 2) h
  simp [Int.add_emod] at this

/-- If the sum of an integer with itself is zero, then the integer is itself zero. -/
lemma zero_of_twice_zero : ∀ a : ℤ, a + a = 0 → a = 0 := by simp

end Int

/-- The only element of `P` with order dividing `2` is the identity. -/
theorem square_free : ∀ {g : P}, g * g = (1 : P) → g = (1 : P)
  | ((p, q, r), x) => by
    match x with
    | .e => aesop (rule_sets [P]) (add norm [Int.zero_of_twice_zero])
    | .a | .b | .c => aesop (rule_sets [P]) (add norm [Int.odd_ne_zero])

/-! ### **Step 4:** Showing that if the `n`-th power of a group element is trivial, then so is that of its square. -/

/-- If `g` is a torsion element of a group, then so is `g ^ 2`. -/
lemma torsion_implies_square_torsion {G : Type _} [Group G] (g : G) (n : ℕ) (g_tor : g ^ n = 1) : (g ^ 2) ^ n = 1 :=
  calc  (g ^ 2) ^ n  = g ^ (2 * n) := by rw [← pow_mul]
              _      = g ^ (n * 2) := by rw [mul_comm]
              _      = (g ^ n) ^ 2 := by rw [pow_mul]
              _      = (1 : G) ^ 2 := by rw [← g_tor]
              _      = (1 : G)     := by rw [one_pow]

/-! ### **Step 5:** Putting the facts together. -/

/-- `P` is torsion-free. -/
instance P.torsionFree : TorsionFree P where
  torsionFree := by
    intros g n g_tor -- assume `g` is a torsion element
    -- then `g ^ 2` is also a torsion element
    have square_tor : (g ^ 2) ^ n.succ = ((0, 0, 0), Q.e) := torsion_implies_square_torsion g n.succ g_tor
    erw [pow_two, sq_square, MetabelianGroup.kernel_pow, Prod.mk.injEq] at square_tor
    -- since `g ^ 2 = s g`, we have that `s g` is a torsion element
    have s_tor : n.succ • (sq g) = 0 := square_tor.left
    -- since `s g` lies in the kernel and the kernel is torsion-free, `s g = 0`
    have square_zero : (sq g, Q.e) = (0, Q.e) := congrArg (·, Q.e) (K.torsionFree.torsionFree (sq g) n s_tor)
    rw [← sq_square] at square_zero
    -- this means `g ^ 2 = e`, and also `g = e` because `P` has no order 2 elements
    exact square_free square_zero


import data.list.basic .tactics

variables {α β : Type}

namespace list

open tactic

def append_pair : (list α × list α) → list α
| (l1,l2) := l1 ++ l2

lemma forsome_mem_cons {p : α → Prop} {a : α} {as : list α} :
  (∃ x, x ∈ (a::as) ∧ p x) ↔ p a ∨ ∃ x, x ∈ as ∧ p x :=
begin
  constructor; intro h;
  [
    {
      cases h with a2 ha2, cases ha2 with ha2l ha2r,
      cases (eq_or_mem_of_mem_cons ha2l) with ha2l,
      subst ha2l, apply or.inl, assumption,
      apply or.inr, existsi a2, constructor; assumption
    },
    {
      cases h with h h;
      [
        { existsi a, constructor, apply or.inl rfl, assumption },
        {
          cases h with a ha, cases ha with ha1 ha2,
          existsi a, apply and.intro (or.inr ha1) ha2
        }
      ]
    }
  ]
end

def forall_mem (P : α → Prop) (as : list α) := ∀ a ∈ as, P a

lemma forall_mem_map_iff {f : α → β} {Q : β → Prop} {as} :
(∀ b ∈ (map f as), Q b) ↔ (∀ a ∈ as, Q (f a)) :=
begin
  constructor; intro h,
  { intros a ha,
    apply h, apply mem_map_of_mem _ ha },
  { intros b hb, rw mem_map at hb,
    cases hb with a ha, cases ha with ha1 ha2,
    subst ha2, apply h _ ha1 }
end

lemma forall_mem_map {f : α → β} {p : β → Prop} {as} :
(∀ a ∈ as, p (f a)) → (∀ b ∈ (map f as), p b) :=
forall_mem_map_iff.elim_right

lemma forall_mem_filter_of_forall_mem {P Q : α → Prop} [H : decidable_pred Q]
  {as : list α} (h : ∀ a ∈ as, P a) : ∀ a ∈ (list.filter Q as), P a :=
begin intros a ha, apply h, apply mem_of_mem_filter ha end

lemma forall_mem_filter {P Q : α → Prop} [H : decidable_pred P] {as} :
(∀ a ∈ (filter P as), Q a) ↔ (∀ a ∈ as, P a → Q a) :=
begin
  constructor; intro h,
  { intros a ha1 ha2, apply h, rw mem_filter,
    constructor; assumption },
  { intros a ha, rw mem_filter at ha,
    apply h _ ha.left ha.right }
end

def some_true (ps : list Prop) : Prop := ∃ p, p ∈ ps ∧ p

def forsome_mem (P : α → Prop) (as : list α) := ∃ a, a ∈ as ∧ P a

instance forsome_mem.decidable {P : α → Prop} [hd : decidable_pred P] :
  ∀ {as : list α}, decidable (∃ a, a ∈ as ∧ P a)
| [] := decidable.is_false (by simp)
| (a::as) :=
  begin
    cases (hd a),
      { cases forsome_mem.decidable,
          { apply decidable.is_false, intro hc,
            cases hc with a' hc, cases hc with hc1 hc2,
            cases hc1, subst hc1, apply h hc2, apply h_1,
            existsi a', constructor; assumption },
          { apply decidable.is_true,
            rw forsome_mem_cons, apply or.inr h_1 } },
      { apply decidable.is_true,
        rw forsome_mem_cons, apply or.inl h }
  end

instance dec_eq_nil : ∀ {as : list α}, decidable (as = [])
| [] := decidable.is_true rfl
| (a::as) := decidable.is_false (begin intro h, cases h end)

lemma map_ne_nil_of_ne_nil :
  ∀ {f : α → β} {as : list α}, as ≠ [] → map f as ≠ [] :=
λ f as, not_imp_not.elim_right eq_nil_of_map_eq_nil

lemma exists_maximum [linear_order β] :
∀ {bs : list β} (hi : bs ≠ []), ∃ b, b ∈ bs ∧ ∀ b' ∈ bs, b' ≤ b
| [] hi := begin exfalso, apply hi rfl end
| [b] hi :=
  begin
    existsi b, apply and.intro (or.inl rfl),
    intros b' hb', cases hb' with hb' hb',
    subst hb', cases hb',
  end
| (b::b'::bs') hi :=
  begin
    cases (@exists_maximum (b'::bs') _) with x hx;
    [{}, { exact_dec_trivial }], cases hx with hx1 hx2,
    apply @classical.by_cases (b ≤ x); intro hle,
    { existsi x, apply and.intro (or.inr hx1),
      intros bl hbl, rewrite mem_cons_iff at hbl,
      cases hbl with hbl hbl, subst hbl, apply hle,
      apply hx2 _ hbl },
    { existsi b, apply and.intro (or.inl rfl),
      intros bl hbl, rewrite mem_cons_iff at hbl,
      cases hbl with hbl hbl, subst hbl,
      apply le_trans, apply hx2 _ hbl,
      apply le_of_not_le hle }
  end

def converse_linear_order (h : linear_order α) : linear_order α :=
{
  le := ge,
  le_refl := le_refl,
  le_trans := @ge_trans _ _,
  le_antisymm := λ a b h1 h2, le_antisymm h2 h1,
  le_total := λ a b, or.symm (le_total a b)
}

lemma exists_minimum [h : linear_order β] :
∀ {bs : list β} (hi : bs ≠ []), ∃ b, b ∈ bs ∧ ∀ b' ∈ bs, b' ≥ b :=
@exists_maximum _ (converse_linear_order h)

def map_neg [has_neg α] (as : list α) : list α :=
list.map (λ x, -x) as

def map_mul [has_mul α] (a) (as : list α) : list α :=
list.map (λ x, a * x) as

lemma of_mem_repeat {k m : nat} :
∀ {n}, k ∈ repeat m n → k = m
| 0 h := by cases h
| (n+1) h :=
  begin
    simp [repeat] at h, cases h, assumption,
    apply eq_of_mem_repeat h
  end

lemma one_map_mul [monoid α] :
∀ (as : list α), map_mul 1 as = as
| [] := rfl
| (a::as) := begin simp [map_mul], apply map_id end

def update_nth_force : list α → ℕ → α → α → list α
| (x::xs) 0     a a' := a :: xs
| (x::xs) (i+1) a a' := x :: update_nth_force xs i a a'
| []      0     a a' := [a]
| []      (i+1) a a' := a' :: update_nth_force [] i a a'

def comp_add [has_add α] : list α → list α → list α
| (a1::as1) (a2::as2) := (a1+a2)::(comp_add as1 as2)
| [] as2 := as2
| as1 [] := as1

def comp_sub [has_neg α] [has_add α] [has_sub α] : list α → list α → list α
| (a1::as1) (a2::as2) := (a1-a2)::(comp_sub as1 as2)
| as1 [] := as1
| [] as2 := map_neg as2

lemma nil_comp_add [semiring α] (as : list α) :
comp_add [] as = as :=
begin cases as; refl end

lemma comp_add_nil [semiring α] (as : list α) :
comp_add as [] = as :=
begin cases as; refl end

lemma nil_comp_sub [has_neg α] [has_add α] [has_sub α] (as : list α) :
comp_sub [] as = map_neg as :=
begin cases as; refl end

lemma comp_sub_nil [has_neg α] [has_add α] [has_sub α] (as : list α) :
comp_sub as [] = as :=
begin cases as; refl end

lemma filter_map_append_eq {f : α → option β} :
∀ {as1 as2 : list α}, filter_map f (as1 ++ as2) = (filter_map f as1 ++ filter_map f as2)
| [] as2 := begin simp [filter_map] end
| (a::as1) as2 :=
  begin
    simp [filter_map], cases (f a),
    apply filter_map_append_eq,
    rw (@filter_map_append_eq as1 as2), refl,
  end

lemma forall_mem_iff_forall_mem {P Q : α → Prop} {as : list α} :
(∀ a, P a ↔ Q a) → ((∀ a ∈ as, P a) ↔ (∀ a ∈ as, Q a)) :=
begin
  intro heq, constructor; intros h a ha,
  rw ← heq, apply h _ ha, rw heq, apply h _ ha
end

end list


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl

! This file was ported from Lean 3 source module order.disjoint
! leanprover-community/mathlib commit 70d50ecfd4900dd6d328da39ab7ebd516abe4025
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Order.BoundedOrder

/-!
# Disjointness and complements

This file defines `Disjoint`, `Codisjoint`, and the `IsCompl` predicate.

## Main declarations

* `Disjoint x y`: two elements of a lattice are disjoint if their `inf` is the bottom element.
* `Codisjoint x y`: two elements of a lattice are codisjoint if their `join` is the top element.
* `IsCompl x y`: In a bounded lattice, predicate for "`x` is a complement of `y`". Note that in a
  non distributive lattice, an element can have several complements.
* `ComplementedLattice α`: Typeclass stating that any element of a lattice has a complement.

-/

variable {α : Type _}

section Disjoint

section PartialOrderBot

variable [PartialOrder α] [OrderBot α] {a b c d : α}

/-- Two elements of a lattice are disjoint if their inf is the bottom element.
  (This generalizes disjoint sets, viewed as members of the subset lattice.)

Note that we define this without reference to `⊓`, as this allows us to talk about orders where
the infimum is not unique, or where implementing `Inf` would require additional `Decidable`
arguments. -/
def Disjoint (a b : α) : Prop :=
  ∀ ⦃x⦄, x ≤ a → x ≤ b → x ≤ ⊥
#align disjoint Disjoint

theorem disjoint_comm : Disjoint a b ↔ Disjoint b a :=
  forall_congr' fun _ ↦ forall_swap
#align disjoint.comm disjoint_comm

@[symm]
theorem Disjoint.symm ⦃a b : α⦄ : Disjoint a b → Disjoint b a :=
  disjoint_comm.1
#align disjoint.symm Disjoint.symm

theorem symmetric_disjoint : Symmetric (Disjoint : α → α → Prop) :=
  Disjoint.symm
#align symmetric_disjoint symmetric_disjoint

@[simp]
theorem disjoint_bot_left : Disjoint ⊥ a := fun _ hbot _ ↦ hbot
#align disjoint_bot_left disjoint_bot_left

@[simp]
theorem disjoint_bot_right : Disjoint a ⊥ := fun _ _ hbot ↦ hbot
#align disjoint_bot_right disjoint_bot_right

theorem Disjoint.mono (h₁ : a ≤ b) (h₂ : c ≤ d) : Disjoint b d → Disjoint a c :=
  fun h _ ha hc ↦ h (ha.trans h₁) (hc.trans h₂)
#align disjoint.mono Disjoint.mono

theorem Disjoint.mono_left (h : a ≤ b) : Disjoint b c → Disjoint a c :=
  Disjoint.mono h le_rfl
#align disjoint.mono_left Disjoint.mono_left

theorem Disjoint.mono_right : b ≤ c → Disjoint a c → Disjoint a b :=
  Disjoint.mono le_rfl
#align disjoint.mono_right Disjoint.mono_right

@[simp]
theorem disjoint_self : Disjoint a a ↔ a = ⊥ :=
  ⟨fun hd ↦ bot_unique <| hd le_rfl le_rfl, fun h _ ha _ ↦ ha.trans_eq h⟩
#align disjoint_self disjoint_self

/- TODO: Rename `Disjoint.eq_bot` to `Disjoint.inf_eq` and `Disjoint.eq_bot_of_self` to
`Disjoint.eq_bot` -/
alias disjoint_self ↔ Disjoint.eq_bot_of_self _
#align disjoint.eq_bot_of_self Disjoint.eq_bot_of_self

theorem Disjoint.ne (ha : a ≠ ⊥) (hab : Disjoint a b) : a ≠ b :=
fun h ↦ ha <| disjoint_self.1 <| by rwa [← h] at hab
#align disjoint.ne Disjoint.ne

theorem Disjoint.eq_bot_of_le (hab : Disjoint a b) (h : a ≤ b) : a = ⊥ :=
  eq_bot_iff.2 <| hab le_rfl h
#align disjoint.eq_bot_of_le Disjoint.eq_bot_of_le

theorem Disjoint.eq_bot_of_ge (hab : Disjoint a b) : b ≤ a → b = ⊥ :=
  hab.symm.eq_bot_of_le
#align disjoint.eq_bot_of_ge Disjoint.eq_bot_of_ge

end PartialOrderBot

section PartialBoundedOrder

variable [PartialOrder α] [BoundedOrder α] {a : α}

@[simp]
theorem disjoint_top : Disjoint a ⊤ ↔ a = ⊥ :=
  ⟨fun h ↦ bot_unique <| h le_rfl le_top, fun h _ ha _ ↦ ha.trans_eq h⟩
#align disjoint_top disjoint_top

@[simp]
theorem top_disjoint : Disjoint ⊤ a ↔ a = ⊥ :=
  ⟨fun h ↦ bot_unique <| h le_top le_rfl, fun h _ _ ha ↦ ha.trans_eq h⟩
#align top_disjoint top_disjoint

end PartialBoundedOrder

section SemilatticeInfBot

variable [SemilatticeInf α] [OrderBot α] {a b c d : α}

theorem disjoint_iff_inf_le : Disjoint a b ↔ a ⊓ b ≤ ⊥ :=
  ⟨fun hd ↦ hd inf_le_left inf_le_right, fun h _ ha hb ↦ (le_inf ha hb).trans h⟩
#align disjoint_iff_inf_le disjoint_iff_inf_le

theorem disjoint_iff : Disjoint a b ↔ a ⊓ b = ⊥ :=
  disjoint_iff_inf_le.trans le_bot_iff
#align disjoint_iff disjoint_iff

theorem Disjoint.le_bot : Disjoint a b → a ⊓ b ≤ ⊥ :=
  disjoint_iff_inf_le.mp
#align disjoint.le_bot Disjoint.le_bot

theorem Disjoint.eq_bot : Disjoint a b → a ⊓ b = ⊥ :=
  bot_unique ∘ Disjoint.le_bot
#align disjoint.eq_bot Disjoint.eq_bot

theorem disjoint_assoc : Disjoint (a ⊓ b) c ↔ Disjoint a (b ⊓ c) := by
  rw [disjoint_iff_inf_le, disjoint_iff_inf_le, inf_assoc]
#align disjoint_assoc disjoint_assoc

theorem disjoint_left_comm : Disjoint a (b ⊓ c) ↔ Disjoint b (a ⊓ c) := by
  simp_rw [disjoint_iff_inf_le, inf_left_comm]
#align disjoint_left_comm disjoint_left_comm

theorem disjoint_right_comm : Disjoint (a ⊓ b) c ↔ Disjoint (a ⊓ c) b := by
  simp_rw [disjoint_iff_inf_le, inf_right_comm]
#align disjoint_right_comm disjoint_right_comm

variable (c)

theorem Disjoint.inf_left (h : Disjoint a b) : Disjoint (a ⊓ c) b :=
  h.mono_left inf_le_left
#align disjoint.inf_left Disjoint.inf_left

theorem Disjoint.inf_left' (h : Disjoint a b) : Disjoint (c ⊓ a) b :=
  h.mono_left inf_le_right
#align disjoint.inf_left' Disjoint.inf_left'

theorem Disjoint.inf_right (h : Disjoint a b) : Disjoint a (b ⊓ c) :=
  h.mono_right inf_le_left
#align disjoint.inf_right Disjoint.inf_right

theorem Disjoint.inf_right' (h : Disjoint a b) : Disjoint a (c ⊓ b) :=
  h.mono_right inf_le_right
#align disjoint.inf_right' Disjoint.inf_right'

variable {c}

theorem Disjoint.of_disjoint_inf_of_le (h : Disjoint (a ⊓ b) c) (hle : a ≤ c) : Disjoint a b :=
  disjoint_iff.2 <| h.eq_bot_of_le <| inf_le_of_left_le hle
#align disjoint.of_disjoint_inf_of_le Disjoint.of_disjoint_inf_of_le

theorem Disjoint.of_disjoint_inf_of_le' (h : Disjoint (a ⊓ b) c) (hle : b ≤ c) : Disjoint a b :=
  disjoint_iff.2 <| h.eq_bot_of_le <| inf_le_of_right_le hle
#align disjoint.of_disjoint_inf_of_le' Disjoint.of_disjoint_inf_of_le'

end SemilatticeInfBot

section DistribLatticeBot

variable [DistribLattice α] [OrderBot α] {a b c : α}

@[simp]
theorem disjoint_sup_left : Disjoint (a ⊔ b) c ↔ Disjoint a c ∧ Disjoint b c := by
  simp only [disjoint_iff, inf_sup_right, sup_eq_bot_iff]
#align disjoint_sup_left disjoint_sup_left

@[simp]
theorem disjoint_sup_right : Disjoint a (b ⊔ c) ↔ Disjoint a b ∧ Disjoint a c := by
  simp only [disjoint_iff, inf_sup_left, sup_eq_bot_iff]
#align disjoint_sup_right disjoint_sup_right

theorem Disjoint.sup_left (ha : Disjoint a c) (hb : Disjoint b c) : Disjoint (a ⊔ b) c :=
  disjoint_sup_left.2 ⟨ha, hb⟩
#align disjoint.sup_left Disjoint.sup_left

theorem Disjoint.sup_right (hb : Disjoint a b) (hc : Disjoint a c) : Disjoint a (b ⊔ c) :=
  disjoint_sup_right.2 ⟨hb, hc⟩
#align disjoint.sup_right Disjoint.sup_right

theorem Disjoint.left_le_of_le_sup_right (h : a ≤ b ⊔ c) (hd : Disjoint a c) : a ≤ b :=
  le_of_inf_le_sup_le (le_trans hd.le_bot bot_le) <| sup_le h le_sup_right
#align disjoint.left_le_of_le_sup_right Disjoint.left_le_of_le_sup_right

theorem Disjoint.left_le_of_le_sup_left (h : a ≤ c ⊔ b) (hd : Disjoint a c) : a ≤ b :=
  hd.left_le_of_le_sup_right <| by rwa [sup_comm]
#align disjoint.left_le_of_le_sup_left Disjoint.left_le_of_le_sup_left

end DistribLatticeBot

end Disjoint

section Codisjoint

section PartialOrderTop

variable [PartialOrder α] [OrderTop α] {a b c d : α}

/-- Two elements of a lattice are codisjoint if their sup is the top element.

Note that we define this without reference to `⊔`, as this allows us to talk about orders where
the supremum is not unique, or where implement `Sup` would require additional `Decidable`
arguments. -/
def Codisjoint (a b : α) : Prop :=
  ∀ ⦃x⦄, a ≤ x → b ≤ x → ⊤ ≤ x
#align codisjoint Codisjoint

theorem Codisjoint_comm : Codisjoint a b ↔ Codisjoint b a :=
  forall_congr' fun _ ↦ forall_swap
#align codisjoint.comm Codisjoint_comm

@[symm]
theorem Codisjoint.symm ⦃a b : α⦄ : Codisjoint a b → Codisjoint b a :=
  Codisjoint_comm.1
#align codisjoint.symm Codisjoint.symm

theorem symmetric_codisjoint : Symmetric (Codisjoint : α → α → Prop) :=
  Codisjoint.symm
#align symmetric_codisjoint symmetric_codisjoint

@[simp]
theorem codisjoint_top_left : Codisjoint ⊤ a := fun _ htop _ ↦ htop
#align codisjoint_top_left codisjoint_top_left

@[simp]
theorem codisjoint_top_right : Codisjoint a ⊤ := fun _ _ htop ↦ htop
#align codisjoint_top_right codisjoint_top_right

theorem Codisjoint.mono (h₁ : a ≤ b) (h₂ : c ≤ d) : Codisjoint a c → Codisjoint b d :=
  fun h _ ha hc ↦ h (h₁.trans ha) (h₂.trans hc)
#align codisjoint.mono Codisjoint.mono

theorem Codisjoint.mono_left (h : a ≤ b) : Codisjoint a c → Codisjoint b c :=
  Codisjoint.mono h le_rfl
#align codisjoint.mono_left Codisjoint.mono_left

theorem Codisjoint.mono_right : b ≤ c → Codisjoint a b → Codisjoint a c :=
  Codisjoint.mono le_rfl
#align codisjoint.mono_right Codisjoint.mono_right

@[simp]
theorem codisjoint_self : Codisjoint a a ↔ a = ⊤ :=
  ⟨fun hd ↦ top_unique <| hd le_rfl le_rfl, fun h _ ha _ ↦ h.symm.trans_le ha⟩
#align codisjoint_self codisjoint_self

/- TODO: Rename `Codisjoint.eq_top` to `Codisjoint.sup_eq` and `Codisjoint.eq_top_of_self` to
`Codisjoint.eq_top` -/
alias codisjoint_self ↔ Codisjoint.eq_top_of_self _
#align codisjoint.eq_top_of_self Codisjoint.eq_top_of_self

theorem Codisjoint.ne (ha : a ≠ ⊤) (hab : Codisjoint a b) : a ≠ b :=
  fun h ↦ ha <| codisjoint_self.1 <| by rwa [← h] at hab
#align codisjoint.ne Codisjoint.ne

theorem Codisjoint.eq_top_of_le (hab : Codisjoint a b) (h : b ≤ a) : a = ⊤ :=
  eq_top_iff.2 <| hab le_rfl h
#align codisjoint.eq_top_of_le Codisjoint.eq_top_of_le

theorem Codisjoint.eq_top_of_ge (hab : Codisjoint a b) : a ≤ b → b = ⊤ :=
  hab.symm.eq_top_of_le
#align codisjoint.eq_top_of_ge Codisjoint.eq_top_of_ge

end PartialOrderTop

section PartialBoundedOrder

variable [PartialOrder α] [BoundedOrder α] {a : α}

@[simp]
theorem codisjoint_bot : Codisjoint a ⊥ ↔ a = ⊤ :=
  ⟨fun h ↦ top_unique <| h le_rfl bot_le, fun h _ ha _ ↦ h.symm.trans_le ha⟩
#align codisjoint_bot codisjoint_bot

@[simp]
theorem bot_codisjoint : Codisjoint ⊥ a ↔ a = ⊤ :=
  ⟨fun h ↦ top_unique <| h bot_le le_rfl, fun h _ _ ha ↦ h.symm.trans_le ha⟩
#align bot_codisjoint bot_codisjoint

end PartialBoundedOrder

section SemilatticeSupTop

variable [SemilatticeSup α] [OrderTop α] {a b c d : α}

theorem codisjoint_iff_le_sup : Codisjoint a b ↔ ⊤ ≤ a ⊔ b :=
  @disjoint_iff_inf_le αᵒᵈ _ _ _ _
#align codisjoint_iff_le_sup codisjoint_iff_le_sup

theorem codisjoint_iff : Codisjoint a b ↔ a ⊔ b = ⊤ :=
  @disjoint_iff αᵒᵈ _ _ _ _
#align codisjoint_iff codisjoint_iff

theorem Codisjoint.top_le : Codisjoint a b → ⊤ ≤ a ⊔ b :=
  @Disjoint.le_bot αᵒᵈ _ _ _ _
#align codisjoint.top_le Codisjoint.top_le

theorem Codisjoint.eq_top : Codisjoint a b → a ⊔ b = ⊤ :=
  @Disjoint.eq_bot αᵒᵈ _ _ _ _
#align codisjoint.eq_top Codisjoint.eq_top

theorem codisjoint_assoc : Codisjoint (a ⊔ b) c ↔ Codisjoint a (b ⊔ c) :=
  @disjoint_assoc αᵒᵈ _ _ _ _ _
#align codisjoint_assoc codisjoint_assoc

theorem codisjoint_left_comm : Codisjoint a (b ⊔ c) ↔ Codisjoint b (a ⊔ c) :=
  @disjoint_left_comm αᵒᵈ _ _ _ _ _
#align codisjoint_left_comm codisjoint_left_comm

theorem codisjoint_right_comm : Codisjoint (a ⊔ b) c ↔ Codisjoint (a ⊔ c) b :=
  @disjoint_right_comm αᵒᵈ _ _ _ _ _
#align codisjoint_right_comm codisjoint_right_comm

variable (c)

theorem Codisjoint.sup_left (h : Codisjoint a b) : Codisjoint (a ⊔ c) b :=
  h.mono_left le_sup_left
#align codisjoint.sup_left Codisjoint.sup_left

theorem Codisjoint.sup_left' (h : Codisjoint a b) : Codisjoint (c ⊔ a) b :=
  h.mono_left le_sup_right
#align codisjoint.sup_left' Codisjoint.sup_left'

theorem Codisjoint.sup_right (h : Codisjoint a b) : Codisjoint a (b ⊔ c) :=
  h.mono_right le_sup_left
#align codisjoint.sup_right Codisjoint.sup_right

theorem Codisjoint.sup_right' (h : Codisjoint a b) : Codisjoint a (c ⊔ b) :=
  h.mono_right le_sup_right
#align codisjoint.sup_right' Codisjoint.sup_right'

variable {c}

theorem Codisjoint.of_codisjoint_sup_of_le (h : Codisjoint (a ⊔ b) c) (hle : c ≤ a) :
    Codisjoint a b :=
  @Disjoint.of_disjoint_inf_of_le αᵒᵈ _ _ _ _ _ h hle
#align codisjoint.of_codisjoint_sup_of_le Codisjoint.of_codisjoint_sup_of_le

theorem Codisjoint.of_codisjoint_sup_of_le' (h : Codisjoint (a ⊔ b) c) (hle : c ≤ b) :
    Codisjoint a b :=
  @Disjoint.of_disjoint_inf_of_le' αᵒᵈ _ _ _ _ _ h hle
#align codisjoint.of_codisjoint_sup_of_le' Codisjoint.of_codisjoint_sup_of_le'

end SemilatticeSupTop

section DistribLatticeTop

variable [DistribLattice α] [OrderTop α] {a b c : α}

@[simp]
theorem codisjoint_inf_left : Codisjoint (a ⊓ b) c ↔ Codisjoint a c ∧ Codisjoint b c := by
  simp only [codisjoint_iff, sup_inf_right, inf_eq_top_iff]
#align codisjoint_inf_left codisjoint_inf_left

@[simp]
theorem codisjoint_inf_right : Codisjoint a (b ⊓ c) ↔ Codisjoint a b ∧ Codisjoint a c := by
  simp only [codisjoint_iff, sup_inf_left, inf_eq_top_iff]
#align codisjoint_inf_right codisjoint_inf_right

theorem Codisjoint.inf_left (ha : Codisjoint a c) (hb : Codisjoint b c) : Codisjoint (a ⊓ b) c :=
  codisjoint_inf_left.2 ⟨ha, hb⟩
#align codisjoint.inf_left Codisjoint.inf_left

theorem Codisjoint.inf_right (hb : Codisjoint a b) (hc : Codisjoint a c) : Codisjoint a (b ⊓ c) :=
  codisjoint_inf_right.2 ⟨hb, hc⟩
#align codisjoint.inf_right Codisjoint.inf_right

theorem Codisjoint.left_le_of_le_inf_right (h : a ⊓ b ≤ c) (hd : Codisjoint b c) : a ≤ c :=
  @Disjoint.left_le_of_le_sup_right αᵒᵈ _ _ _ _ _ h hd.symm
#align codisjoint.left_le_of_le_inf_right Codisjoint.left_le_of_le_inf_right

theorem Codisjoint.left_le_of_le_inf_left (h : b ⊓ a ≤ c) (hd : Codisjoint b c) : a ≤ c :=
  hd.left_le_of_le_inf_right <| by rwa [inf_comm]
#align codisjoint.left_le_of_le_inf_left Codisjoint.left_le_of_le_inf_left

end DistribLatticeTop

end Codisjoint

open OrderDual

theorem Disjoint.dual [SemilatticeInf α] [OrderBot α] {a b : α} :
    Disjoint a b → Codisjoint (toDual a) (toDual b) :=
  id
#align disjoint.dual Disjoint.dual

theorem Codisjoint.dual [SemilatticeSup α] [OrderTop α] {a b : α} :
    Codisjoint a b → Disjoint (toDual a) (toDual b) :=
  id
#align codisjoint.dual Codisjoint.dual

@[simp]
theorem disjoint_toDual_iff [SemilatticeSup α] [OrderTop α] {a b : α} :
    Disjoint (toDual a) (toDual b) ↔ Codisjoint a b :=
  Iff.rfl
#align disjoint_to_dual_iff disjoint_toDual_iff

@[simp]
theorem disjoint_ofDual_iff [SemilatticeInf α] [OrderBot α] {a b : αᵒᵈ} :
    Disjoint (ofDual a) (ofDual b) ↔ Codisjoint a b :=
  Iff.rfl
#align disjoint_of_dual_iff disjoint_ofDual_iff

@[simp]
theorem codisjoint_toDual_iff [SemilatticeInf α] [OrderBot α] {a b : α} :
    Codisjoint (toDual a) (toDual b) ↔ Disjoint a b :=
  Iff.rfl
#align codisjoint_to_dual_iff codisjoint_toDual_iff

@[simp]
theorem codisjoint_ofDual_iff [SemilatticeSup α] [OrderTop α] {a b : αᵒᵈ} :
    Codisjoint (ofDual a) (ofDual b) ↔ Disjoint a b :=
  Iff.rfl
#align codisjoint_of_dual_iff codisjoint_ofDual_iff

section DistribLattice

variable [DistribLattice α] [BoundedOrder α] {a b c : α}

theorem Disjoint.le_of_codisjoint (hab : Disjoint a b) (hbc : Codisjoint b c) : a ≤ c := by
  rw [← @inf_top_eq _ _ _ a, ← @bot_sup_eq _ _ _ c, ← hab.eq_bot, ← hbc.eq_top, sup_inf_right]
  exact inf_le_inf_right _ le_sup_left
#align disjoint.le_of_codisjoint Disjoint.le_of_codisjoint

end DistribLattice

section IsCompl

/-- Two elements `x` and `y` are complements of each other if `x ⊔ y = ⊤` and `x ⊓ y = ⊥`. -/
structure IsCompl [PartialOrder α] [BoundedOrder α] (x y : α) : Prop where
  /-- If `x` and `y` are to be complementary in an order, they should be disjoint. -/
  protected Disjoint : Disjoint x y
  /-- If `x` and `y` are to be complementary in an order, they should be codisjoint. -/
  protected Codisjoint : Codisjoint x y
#align is_compl IsCompl

theorem isCompl_iff [PartialOrder α] [BoundedOrder α] {a b : α} :
    IsCompl a b ↔ Disjoint a b ∧ Codisjoint a b :=
  ⟨fun h ↦ ⟨h.1, h.2⟩, fun h ↦ ⟨h.1, h.2⟩⟩
#align is_compl_iff isCompl_iff

namespace IsCompl

section BoundedPartialOrder

variable [PartialOrder α] [BoundedOrder α] {x y z : α}

@[symm]
protected theorem symm (h : IsCompl x y) : IsCompl y x :=
  ⟨h.1.symm, h.2.symm⟩
#align is_compl.symm IsCompl.symm

theorem dual (h : IsCompl x y) : IsCompl (toDual x) (toDual y) :=
  ⟨h.2, h.1⟩
#align is_compl.dual IsCompl.dual

theorem ofDual {a b : αᵒᵈ} (h : IsCompl a b) : IsCompl (ofDual a) (ofDual b) :=
  ⟨h.2, h.1⟩
#align is_compl.of_dual IsCompl.ofDual

end BoundedPartialOrder

section BoundedLattice

variable [Lattice α] [BoundedOrder α] {x y z : α}

theorem of_le (h₁ : x ⊓ y ≤ ⊥) (h₂ : ⊤ ≤ x ⊔ y) : IsCompl x y :=
  ⟨disjoint_iff_inf_le.mpr h₁, codisjoint_iff_le_sup.mpr h₂⟩
#align is_compl.of_le IsCompl.of_le

theorem of_eq (h₁ : x ⊓ y = ⊥) (h₂ : x ⊔ y = ⊤) : IsCompl x y :=
  ⟨disjoint_iff.mpr h₁, codisjoint_iff.mpr h₂⟩
#align is_compl.of_eq IsCompl.of_eq

theorem inf_eq_bot (h : IsCompl x y) : x ⊓ y = ⊥ :=
  h.Disjoint.eq_bot
#align is_compl.inf_eq_bot IsCompl.inf_eq_bot

theorem sup_eq_top (h : IsCompl x y) : x ⊔ y = ⊤ :=
  h.Codisjoint.eq_top
#align is_compl.sup_eq_top IsCompl.sup_eq_top

end BoundedLattice

variable [DistribLattice α] [BoundedOrder α] {a b x y z : α}

theorem inf_left_le_of_le_sup_right (h : IsCompl x y) (hle : a ≤ b ⊔ y) : a ⊓ x ≤ b :=
  calc
    a ⊓ x ≤ (b ⊔ y) ⊓ x := inf_le_inf hle le_rfl
    _ = b ⊓ x ⊔ y ⊓ x := inf_sup_right
    _ = b ⊓ x := by rw [h.symm.inf_eq_bot, sup_bot_eq]
    _ ≤ b := inf_le_left

#align is_compl.inf_left_le_of_le_sup_right IsCompl.inf_left_le_of_le_sup_right

theorem le_sup_right_iff_inf_left_le {a b} (h : IsCompl x y) : a ≤ b ⊔ y ↔ a ⊓ x ≤ b :=
  ⟨h.inf_left_le_of_le_sup_right, h.symm.dual.inf_left_le_of_le_sup_right⟩
#align is_compl.le_sup_right_iff_inf_left_le IsCompl.le_sup_right_iff_inf_left_le

theorem inf_left_eq_bot_iff (h : IsCompl y z) : x ⊓ y = ⊥ ↔ x ≤ z := by
  rw [← le_bot_iff, ← h.le_sup_right_iff_inf_left_le, bot_sup_eq]
#align is_compl.inf_left_eq_bot_iff IsCompl.inf_left_eq_bot_iff

theorem inf_right_eq_bot_iff (h : IsCompl y z) : x ⊓ z = ⊥ ↔ x ≤ y :=
  h.symm.inf_left_eq_bot_iff
#align is_compl.inf_right_eq_bot_iff IsCompl.inf_right_eq_bot_iff

theorem disjoint_left_iff (h : IsCompl y z) : Disjoint x y ↔ x ≤ z := by
  rw [disjoint_iff]
  exact h.inf_left_eq_bot_iff
#align is_compl.disjoint_left_iff IsCompl.disjoint_left_iff

theorem disjoint_right_iff (h : IsCompl y z) : Disjoint x z ↔ x ≤ y :=
  h.symm.disjoint_left_iff
#align is_compl.disjoint_right_iff IsCompl.disjoint_right_iff

theorem le_left_iff (h : IsCompl x y) : z ≤ x ↔ Disjoint z y :=
  h.disjoint_right_iff.symm
#align is_compl.le_left_iff IsCompl.le_left_iff

theorem le_right_iff (h : IsCompl x y) : z ≤ y ↔ Disjoint z x :=
  h.symm.le_left_iff
#align is_compl.le_right_iff IsCompl.le_right_iff

theorem left_le_iff (h : IsCompl x y) : x ≤ z ↔ Codisjoint z y :=
  h.dual.le_left_iff
#align is_compl.left_le_iff IsCompl.left_le_iff

theorem right_le_iff (h : IsCompl x y) : y ≤ z ↔ Codisjoint z x :=
  h.symm.left_le_iff
#align is_compl.right_le_iff IsCompl.right_le_iff

protected theorem Antitone {x' y'} (h : IsCompl x y) (h' : IsCompl x' y') (hx : x ≤ x') : y' ≤ y :=
  h'.right_le_iff.2 <| h.symm.Codisjoint.mono_right hx
#align is_compl.antitone IsCompl.Antitone

theorem right_unique (hxy : IsCompl x y) (hxz : IsCompl x z) : y = z :=
  le_antisymm (hxz.Antitone hxy <| le_refl x) (hxy.Antitone hxz <| le_refl x)
#align is_compl.right_unique IsCompl.right_unique

theorem left_unique (hxz : IsCompl x z) (hyz : IsCompl y z) : x = y :=
  hxz.symm.right_unique hyz.symm
#align is_compl.left_unique IsCompl.left_unique

theorem sup_inf {x' y'} (h : IsCompl x y) (h' : IsCompl x' y') : IsCompl (x ⊔ x') (y ⊓ y') :=
  of_eq
    (by rw [inf_sup_right, ← inf_assoc, h.inf_eq_bot, bot_inf_eq, bot_sup_eq, inf_left_comm,
      h'.inf_eq_bot, inf_bot_eq])
    (by rw [sup_inf_left, @sup_comm _ _ x, sup_assoc, h.sup_eq_top, sup_top_eq, top_inf_eq,
      sup_assoc, sup_left_comm, h'.sup_eq_top, sup_top_eq])
#align is_compl.sup_inf IsCompl.sup_inf

theorem inf_sup {x' y'} (h : IsCompl x y) (h' : IsCompl x' y') : IsCompl (x ⊓ x') (y ⊔ y') :=
  (h.symm.sup_inf h'.symm).symm
#align is_compl.inf_sup IsCompl.inf_sup

end IsCompl

namespace Prod

variable {β : Type _} [PartialOrder α] [PartialOrder β]

protected theorem disjoint_iff [OrderBot α] [OrderBot β] {x y : α × β} :
    Disjoint x y ↔ Disjoint x.1 y.1 ∧ Disjoint x.2 y.2 := by
  constructor
  · intro h
    refine' ⟨fun a hx hy ↦ (@h (a, ⊥) ⟨hx, _⟩ ⟨hy, _⟩).1,
      fun b hx hy ↦ (@h (⊥, b) ⟨_, hx⟩ ⟨_, hy⟩).2⟩
    all_goals exact bot_le

  · rintro ⟨ha, hb⟩ z hza hzb
    refine' ⟨ha hza.1 hzb.1, hb hza.2 hzb.2⟩

#align prod.disjoint_iff Prod.disjoint_iff

protected theorem codisjoint_iff [OrderTop α] [OrderTop β] {x y : α × β} :
    Codisjoint x y ↔ Codisjoint x.1 y.1 ∧ Codisjoint x.2 y.2 :=
  @Prod.disjoint_iff αᵒᵈ βᵒᵈ _ _ _ _ _ _
#align prod.codisjoint_iff Prod.codisjoint_iff

protected theorem isCompl_iff [BoundedOrder α] [BoundedOrder β] {x y : α × β} :
    IsCompl x y ↔ IsCompl x.1 y.1 ∧ IsCompl x.2 y.2 := by
  simp_rw [isCompl_iff, Prod.disjoint_iff, Prod.codisjoint_iff, and_and_and_comm]
#align prod.is_compl_iff Prod.isCompl_iff

end Prod

section

variable [Lattice α] [BoundedOrder α] {a b x : α}

@[simp]
theorem isCompl_toDual_iff : IsCompl (toDual a) (toDual b) ↔ IsCompl a b :=
  ⟨IsCompl.ofDual, IsCompl.dual⟩
#align is_compl_to_dual_iff isCompl_toDual_iff

@[simp]
theorem isCompl_ofDual_iff {a b : αᵒᵈ} : IsCompl (ofDual a) (ofDual b) ↔ IsCompl a b :=
  ⟨IsCompl.dual, IsCompl.ofDual⟩
#align is_compl_of_dual_iff isCompl_ofDual_iff

theorem isCompl_bot_top : IsCompl (⊥ : α) ⊤ :=
  IsCompl.of_eq bot_inf_eq sup_top_eq
#align is_compl_bot_top isCompl_bot_top

theorem isCompl_top_bot : IsCompl (⊤ : α) ⊥ :=
  IsCompl.of_eq inf_bot_eq top_sup_eq
#align is_compl_top_bot isCompl_top_bot

theorem eq_top_of_isCompl_bot (h : IsCompl x ⊥) : x = ⊤ :=
  sup_bot_eq.symm.trans h.sup_eq_top
#align eq_top_of_is_compl_bot eq_top_of_isCompl_bot

theorem eq_top_of_bot_isCompl (h : IsCompl ⊥ x) : x = ⊤ :=
  eq_top_of_isCompl_bot h.symm
#align eq_top_of_bot_is_compl eq_top_of_bot_isCompl

theorem eq_bot_of_isCompl_top (h : IsCompl x ⊤) : x = ⊥ :=
  eq_top_of_isCompl_bot h.dual
#align eq_bot_of_is_compl_top eq_bot_of_isCompl_top

theorem eq_bot_of_top_isCompl (h : IsCompl ⊤ x) : x = ⊥ :=
  eq_top_of_bot_isCompl h.dual
#align eq_bot_of_top_is_compl eq_bot_of_top_isCompl

end

/-- A complemented bounded lattice is one where every element has a (not necessarily unique)
complement. -/
class ComplementedLattice (α) [Lattice α] [BoundedOrder α] : Prop where
  /-- In a `ComplementedLattice`, every element admits a complement. -/
  exists_isCompl : ∀ a : α, ∃ b : α, IsCompl a b
#align complemented_lattice ComplementedLattice

export ComplementedLattice (exists_isCompl)

namespace ComplementedLattice

variable [Lattice α] [BoundedOrder α] [ComplementedLattice α]

instance : ComplementedLattice αᵒᵈ :=
  ⟨fun a ↦
    let ⟨b, hb⟩ := exists_isCompl (show α from a)
    ⟨b, hb.dual⟩⟩

end ComplementedLattice

end IsCompl


/-
Copyright (c) 2020 Yury Kudryashov All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov, Heather Macbeth
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.analysis.normed_space.operator_norm
import Mathlib.analysis.normed_space.extend
import Mathlib.analysis.convex.cone
import Mathlib.data.complex.is_R_or_C
import Mathlib.PostPort

universes u_1 u_2 u v

namespace Mathlib

/-!
# Hahn-Banach theorem

In this file we prove a version of Hahn-Banach theorem for continuous linear
functions on normed spaces over `ℝ` and `ℂ`.

In order to state and prove its corollaries uniformly, we prove the statements for a field `𝕜`
satisfying `is_R_or_C 𝕜`.

In this setting, `exists_dual_vector` states that, for any nonzero `x`, there exists a continuous
linear form `g` of norm `1` with `g x = ∥x∥` (where the norm has to be interpreted as an element
of `𝕜`).

-/

/--
The norm of `x` as an element of `𝕜` (a normed algebra over `ℝ`). This is needed in particular to
state equalities of the form `g x = norm' 𝕜 x` when `g` is a linear function.

For the concrete cases of `ℝ` and `𝕜`, this is just `∥x∥` and `↑∥x∥`, respectively.
-/
def norm' (𝕜 : Type u_1) [nondiscrete_normed_field 𝕜] [normed_algebra ℝ 𝕜] {E : Type u_2} [normed_group E] (x : E) : 𝕜 :=
  coe_fn (algebra_map ℝ 𝕜) (norm x)

theorem norm'_def (𝕜 : Type u_1) [nondiscrete_normed_field 𝕜] [normed_algebra ℝ 𝕜] {E : Type u_2} [normed_group E] (x : E) : norm' 𝕜 x = coe_fn (algebra_map ℝ 𝕜) (norm x) :=
  rfl

theorem norm_norm' (𝕜 : Type u_1) [nondiscrete_normed_field 𝕜] [normed_algebra ℝ 𝕜] (A : Type u_2) [normed_group A] (x : A) : norm (norm' 𝕜 x) = norm x := sorry

namespace real

/-- Hahn-Banach theorem for continuous linear functions over `ℝ`. -/
theorem exists_extension_norm_eq {E : Type u_1} [normed_group E] [normed_space ℝ E] (p : subspace ℝ E) (f : continuous_linear_map ℝ ↥p ℝ) : ∃ (g : continuous_linear_map ℝ E ℝ), (∀ (x : ↥p), coe_fn g ↑x = coe_fn f x) ∧ norm g = norm f := sorry

end real

/-- Hahn-Banach theorem for continuous linear functions over `𝕜` satisyfing `is_R_or_C 𝕜`. -/
theorem exists_extension_norm_eq {𝕜 : Type u_1} [is_R_or_C 𝕜] {F : Type u_2} [normed_group F] [normed_space 𝕜 F] (p : subspace 𝕜 F) (f : continuous_linear_map 𝕜 (↥p) 𝕜) : ∃ (g : continuous_linear_map 𝕜 F 𝕜), (∀ (x : ↥p), coe_fn g ↑x = coe_fn f x) ∧ norm g = norm f := sorry

theorem coord_norm' {𝕜 : Type v} [is_R_or_C 𝕜] {E : Type u} [normed_group E] [normed_space 𝕜 E] (x : E) (h : x ≠ 0) : norm (norm' 𝕜 x • continuous_linear_equiv.coord 𝕜 x h) = 1 := sorry

/-- Corollary of Hahn-Banach.  Given a nonzero element `x` of a normed space, there exists an
    element of the dual space, of norm `1`, whose value on `x` is `∥x∥`. -/
theorem exists_dual_vector {𝕜 : Type v} [is_R_or_C 𝕜] {E : Type u} [normed_group E] [normed_space 𝕜 E] (x : E) (h : x ≠ 0) : ∃ (g : continuous_linear_map 𝕜 E 𝕜), norm g = 1 ∧ coe_fn g x = norm' 𝕜 x := sorry

/-- Variant of Hahn-Banach, eliminating the hypothesis that `x` be nonzero, and choosing
    the dual element arbitrarily when `x = 0`. -/
theorem exists_dual_vector' {𝕜 : Type v} [is_R_or_C 𝕜] {E : Type u} [normed_group E] [normed_space 𝕜 E] [nontrivial E] (x : E) : ∃ (g : continuous_linear_map 𝕜 E 𝕜), norm g = 1 ∧ coe_fn g x = norm' 𝕜 x := sorry


import order.filter.archimedean
import data.complex.basic
import topology.instances.nnreal
import analysis.complex.basic
import order.filter.at_top_bot
import analysis.normed.group.infinite_sum
universes u v w

noncomputable theory

open complex metric open_locale big_operators nnreal classical filter

variables {α : Type u} {β : Type v}

lemma summable_if_complex_abs_summable {f : α → ℂ} :
  summable (λ x, complex.abs (f x)) →  summable f :=
begin
  intro h,
  apply summable_of_norm_bounded  (λ x, complex.abs (f x))  h,
  intro i,
  unfold norm,
  exact complete_of_proper,
end

lemma M_test_summable (F : ℕ → α → ℂ) (M : ℕ → ℝ)
  (h1 : ∀ (n : ℕ), ∀ (a : α), (complex.abs (F n a)) ≤ (M n))
  (h2 : summable M) : (∀ (a : α), summable (λ (n : ℕ), F n a)) :=
begin
  intro a,
  apply summable_if_complex_abs_summable,
  have c1 : ∀ (n : ℕ), 0 ≤ (complex.abs (F n a)), by {intro n, apply complex.abs.nonneg (F n a),},
  have H1 : ∀ (n : ℕ), (complex.abs (F n a)) ≤ (M n), by {simp only [h1, forall_const]},
  apply summable_of_nonneg_of_le c1 H1,
  exact h2,
end

lemma sum_sub_tsum_nat_add  {f : ℕ → ℂ} (k : ℕ) (h : summable f) :
 ∑' i, f i - (∑ i in finset.range k, f i) = (∑' i, f (i + k))  :=
begin
  have:= sum_add_tsum_nat_add  k h,
  exact sub_eq_of_eq_add' (eq.symm this),
end

lemma abs_tsum (f : ℕ → ℂ) (h : summable (λ (i : ℕ), complex.abs(f i) )) :
  complex.abs (∑'(i : ℕ), f i ) ≤  (∑' (i : ℕ), complex.abs (f i)) :=
begin
  rw ← complex.norm_eq_abs,
  simp_rw ← complex.norm_eq_abs,
  apply norm_tsum_le_tsum_norm,
  exact h,
end

lemma abs_tsum' {f : α → ℂ} (h : summable (λ (i : α), complex.abs( f i) )) :
  complex.abs (∑'(i : α), f i ) ≤  (∑' (i : α), complex.abs (f i)) :=
begin
  rw ← complex.norm_eq_abs,
  simp_rw ← complex.norm_eq_abs,
  apply norm_tsum_le_tsum_norm,
  exact h,
end

example (r : ℝ) : complex.abs (r : ℂ)= |r| :=
begin
  exact abs_of_real r,
end

lemma M_test_uniform (h : nonempty α) (F : ℕ → α → ℂ) (M : ℕ → ℝ)
  (h1 : ∀ (n : ℕ), ∀ (a : α), (complex.abs (F n a)) ≤ (M n))
  (h2 : summable M) :
  tendsto_uniformly (λ (n : ℕ), (λ (a : α), ∑ i in (finset.range n), F i a))
  ( (λ (a : α), ∑' (n : ℕ), F n a)) filter.at_top :=
begin
  have Mpos: ∀ (n : ℕ), 0 ≤ M n ,
    by {intro n,
    have := h1 n,
    have t1 : ∀ (a : α), 0 ≤  complex.abs(F n a), by {intro a, apply complex.abs.nonneg,},
    apply le_trans (t1 _) (this _),
    have ne := exists_true_iff_nonempty.2 h,
    use classical.some ne,},
  rw metric.tendsto_uniformly_iff,
  intros ε hε,
  have hS := M_test_summable F M h1 h2,
  simp only [filter.eventually_at_top, gt_iff_lt, ge_iff_le] at *,
  have H := summable_iff_vanishing_norm.1 h2 ε hε,
  simp only at H,
  have HU : ∃ (a : ℕ), ∀ (b : ℕ), a ≤ b → | ∑' i, M (i+b) | < ε,
     by { have HC := tendsto_sum_nat_add M,
     simp [tendsto_iff_dist_tendsto_zero] at HC,
     simp only [dist_zero_right, norm_norm] at HC,
     simp_rw metric.tendsto_nhds at HC,
     simp only [filter.eventually_at_top, gt_iff_lt, ge_iff_le, dist_zero_right, norm_norm] at HC,
     simp at *,
     have HXX := (HC ε hε),
     obtain ⟨a, ha⟩ := HXX,
     refine ⟨a, _⟩,
     intros b hb,
     convert ha b hb,},
  have c1 : ∀ (a : α) (n : ℕ), 0 ≤ (complex.abs (F n a)),
  by {intros a n, apply complex.abs.nonneg (F n a),},
  have H1 : ∀ (a : α) (n : ℕ), complex.abs (F n a) ≤ (M n), by {simp [h1]},
  have B1 : ∀ (a : α), ∑' (n : ℕ), complex.abs(F n a) ≤ ∑' (n : ℕ), M n,
    by { intro a,
    apply tsum_le_tsum,
    simp only [h1, forall_const],
    apply summable_of_nonneg_of_le (c1 a) (H1 a) h2,
    exact h2,},
  have S1 : ∀ (a : α), summable (λ (i : ℕ), complex.abs (F i a)),
  by  {intro a, apply summable_of_nonneg_of_le (c1 a) (H1 a) h2,},
  have BU : ∃ (a : ℕ), ∀ (b : ℕ), a ≤ b → ∀ (r : α),  ∑' i, complex.abs (F (i+b) r)  < ε,
    by {cases HU,
    use HU_w,
    intros b hb,
    intro r,
    have :  ∑' i, complex.abs (F (i+b) r)  ≤ |∑' i, M (i+b) |,
      by { have r1 : | ∑' i, M (i+b) | =∑' i, M (i+b),
        by {apply real.norm_of_nonneg,
        apply  tsum_nonneg,
        simp only [Mpos, forall_const],},
      rw r1,
      apply tsum_le_tsum,
      simp only [h1, forall_const],
      apply (summable_nat_add_iff b).2 (S1 r),
      apply (summable_nat_add_iff b).2 h2,},
    cases H,
    cases h2,
    dsimp at *,
    have hut := HU_h b hb, exact gt_of_gt_of_ge (HU_h b hb) this,},
  have H2 : ∀ (a : α) (k : ℕ),
  ∑' (n : ℕ), F n a - ∑ (i : ℕ) in finset.range k, F i a = ∑' (n : ℕ), F (n+k) a,
    by {intros a k,
    apply  sum_sub_tsum_nat_add k,
    exact hS a,},
  simp_rw dist_eq_norm,
  simp_rw H2,
  simp only [norm_eq_abs] at *,
  cases BU,
  use BU_w,
  intros b hb r,
  have BUC := BU_h b hb r,
  let G := (λ (i : ℕ), F (i) r),
  have f_um := abs_tsum (λ (i : ℕ), F (i+b) r) _,
  exact gt_of_gt_of_ge BUC f_um,
  have f_sum := (S1 r),
  apply (summable_nat_add_iff b).2 f_sum,
end


import .main

open tactic

#exit
meta def ex01 : expr := `(∀ x : int, x ≤ -x → x ≤ 0)
meta def ex02 : expr := `(∀ x y : int, (x ≤ 5 ∧ y ≤ 3) → x + y ≤ 8)
meta def ex03 : expr := `(∀ x y z : int, x < y → y < z → x < z)
meta def ex04 : expr := `(∀ x y z : int, x - y ≤ x - z → z ≤ y)
meta def ex05 : expr := `(∀ x : int, (x = 5 ∨ x = 7) → 2 < x)
meta def ex06 : expr := `(∀ x : int, (x = -5 ∨ x = 7) → x ≠ 0)
meta def ex07 : expr := `(∀ x : int, 31 * x > 0 → x > 0)
meta def ex08 : expr := `(∀ x y : int, (-x - y < x - y) → (x - y < x + y) → (x > 0 ∧ y > 0))
meta def ex09 : expr := `(∀ x y : int, ¬(2 * x + 1 = 2 * y))
meta def ex10 : expr := `(∀ x y z : int, (2 * x + 1 = 2 * y) → x + y + z > 1)
meta def ex11 : expr := `(∃ x y : int, 5 * x + 3 * y = 1)
meta def ex12 : expr := `(∀ x y : int, x + 2 < y → ∃ z w : int, (x < z ∧ z < w ∧ w < y))
meta def ex13 : expr := `(∀ x : int, (x ≥ -1 ∧ x ≤ 1) → (x = -1 ∨ x = 0 ∨ x = 1))
meta def ex14 : expr := `(∀ x : int, ∃ y : int, x = 2 * y ∨ x = (2 * y) + 1)
meta def ex15 : expr := `(∀ x : int, 5 * x = 5 → x = 1)

meta def ex16 : expr := `(∀ x : int, (∃ y : int, 2 * y + 1 = x) → ¬ ∃ y : int, 4 * y = x)
meta def ex17 : expr := `(∃ x : int, 5 * x = 1335)
meta def ex18 : expr := `(∃ x y : int, x + y = 231 ∧ x - y = -487)
meta def ex19 : expr := `(∀ a : int, ∃ b : int, a < 4 * b + 3 * a ∨ (¬(a < b) ∧ a > b + 1))
meta def ex20 : expr := `(∃ x y : int, x > 0 ∧ y ≥ 0 ∧ 3 * x - 5 * y = 1)
meta def ex21 : expr := `(∃ x y : int, x ≥ 0 ∧ y ≥ 0 ∧ 5 * x - 6 * y = 1)
meta def ex22 : expr := `(∃ x y : int, x ≥ 0 ∧ y ≥ 0 ∧ 5 * x - 3 * y = 1)
meta def ex23 : expr := `(∃ x y : int, x ≥ 0 ∧ y ≥ 0 ∧ 3 * x - 5 * y = 1)
meta def ex24 : expr := `(∃ a b : int, ¬(a = 1) ∧ ((2 * b = a) ∨ (2 * b = 3 * a + 1)) ∧ (a = b))
meta def ex25 : expr := `(∀ x y z : int, x = y → y = z → x = z)
meta def ex26 : expr := `(∀ x : int, x < 349 ∨ x > 123)
meta def ex27 : expr := `(∀ x y : int, x ≤ 3 * y → 3 * x ≤ 9 * y)
meta def ex28 : expr := `(∃ x y : int, 32 * x = 2023 + y)
meta def ex29 : expr := `(∀ x : int, (x < 43 ∧ x > 513) → x ≠ x)
meta def ex30 : expr := `(∀ x : int, ∃ y : int, x = 3 * y - 1 ∨ x = 3 * y ∨ x = 3 * y + 1)
meta def ex31 : expr := `(forall x : int, exists y : int, x = 5 * y - 2 \/ x = 5 * y - 1 \/ x = 5 * y \/ x = 5 * y + 1 \/ x = 5 * y + 2)
meta def ex32 : expr := `(forall x y : int, 6 * x = 5 * y -> exists d : int, y = 3 * d)
meta def ex33 : expr := `(forall x : int, ¬(exists m : int, x = 2 * m) /\ (exists m : int, x = 3 * m + 1) ↔ (exists m : int, x = 12 * m + 1) \/ (exists m : int, x = 12 * m + 7))
meta def ex34 : expr := `(forall x y : int, (exists d : int, x + y = 2 * d) ↔ ((exists d : int, x = 2 * d) ↔ (exists d : int, y = 2 * d)))

meta def ex35 : expr := `(∀ x : int, x > 5000 → ∃ y : int, y ≥ 1000 ∧ 5 * y < x)
meta def ex36 : expr := `(forall x : int, (exists y : int, 3 * y = x) -> (exists y : int, 7 * y = x) -> (exists y : int, 21 * y = x))
meta def ex37 : expr := `(forall y : int, (exists d : int, y = 65 * d) -> (exists d : int, y = 5 * d))
meta def ex38 : expr :=
  `(forall n : int, 0 < n /\ n < 2400
    -> n <= 2 /\ 2 <= 2 * n \/
       n <= 3 /\ 3 <= 2 * n \/
       n <= 5 /\ 5 <= 2 * n \/
       n <= 7 /\ 7 <= 2 * n \/
       n <= 13 /\ 13 <= 2 * n \/
       n <= 23 /\ 23 <= 2 * n \/
       n <= 43 /\ 43 <= 2 * n \/
       n <= 83 /\ 83 <= 2 * n \/
       n <= 163 /\ 163 <= 2 * n \/
       n <= 317 /\ 317 <= 2 * n \/
       n <= 631 /\ 631 <= 2 * n \/
       n <= 1259 /\ 1259 <= 2 * n \/
       n <= 2503 /\ 2503 <= 2 * n)
meta def ex39 : expr := `(forall z : int, z > 7 -> exists x y : int, x >= 0 /\ y >= 0 /\ 3 * x + 5 * y = z)
meta def ex40 : expr := `(exists w x y z : int, 2 * w + 3 * x + 4 * y + 5 * z = 1)
meta def ex41 : expr := `(forall x : int, x >= 8 -> exists u v : int, u >= 0 /\ v >= 0 /\ x = 3 * u + 5 * v)
meta def ex42 : expr :=  `(forall x y : int, x <= y -> 2 * x + 1 < 2 * y)
meta def ex43 : expr :=  `(forall a b : int, exists x : int, a < 20 * x /\ 20 * x < b)
meta def ex44 : expr :=  `(exists y : int, forall x : int, x + 5 * y > 1 /\ 13 * x - y > 1 /\ x + 2 < 0)
meta def ex45 : expr :=  `(exists x y : int, 5 * x + 10 * y = 1)
meta def ex46 : expr :=  `(forall x y : int, x >= 0 /\ y >= 0           -> 12 * x - 8 * y < 0 \/ 12 * x - 8 * y > 2)
meta def ex47 : expr :=  `(exists x y : int, x >= 0 /\ y >= 0 /\ 6 * x - 3 * y = 1)
meta def ex48 : expr :=  `(forall x y : int, ¬(x = 0) -> 5 * y < 6 * x \/ 5 * y > 6 * x)
meta def ex49 : expr :=  `(forall x y : int, ¬(6 * x = 5 * y))
meta def ex50 : expr :=  `(exists a b : int, a > 1 /\ b > 1 /\ ((2 * b = a) \/ (2 * b = 3 * a + 1)) /\ (a = b))

meta def batch_test (solver : tactic unit) : nat → list expr → tactic unit
| _ [] := tactic.triv
| idx (exp::exps) :=
  ((do gs ← tactic.get_goals,
       g ← tactic.mk_meta_var exp,
       tactic.set_goals (g::gs),
       solver) <|> (trace (("Failed ex " : format) ++ format.of_nat idx) >> skip))
       >> batch_test (idx+1) exps

meta def examples_easy : list expr :=
[ex01,ex02,ex03,ex04,ex05,ex06,ex07,ex08,ex09,ex10,
 ex11,ex12,ex13,ex14,ex15,ex16,ex17,ex18,ex19,ex20,
 ex21,ex22,ex23,ex24,ex25,ex26,ex27,ex28,ex29,ex30,
 ex31,ex32,ex33,ex34]

meta def examples_hard : list expr :=
[ex35,ex36,ex37,ex38,ex39,ex40,ex41]

meta def nontheorems : list expr :=
[ex42,ex43,ex44,ex45,ex46,ex47,ex48,ex49,ex50]

set_option profiler true

lemma test : true :=
by do batch_test lia_vm 0 nontheorems

-- lemma test_lia_vm : true :=
-- by do batch_test lia_vm 0 examples_easy


/-
Copyright (c) 2019 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro

! This file was ported from Lean 3 source module data.rat.init
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Tactic.Ext
import Mathbin.Logic.Basic

/-!
# The definition of the Rational Numbers

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

## Summary

We define a rational number `q` as a structure `{ num, denom, pos, cop }`, where
- `num` is the numerator of `q`,
- `denom` is the denominator of `q`,
- `pos` is a proof that `denom > 0`, and
- `cop` is a proof `num` and `denom` are coprime.

Basic constructions and results are set up in `data.rat.defs`.
As we transition to Lean 4, these two files can probably be merged again,
as so much of the needed material will be in Std4 anyway.

For now, this split allows us to give the definitions of division rings and fields
without significant theory imports.

The definition of the field structure on `ℚ` will be done in `data.rat.basic` once the
`field` class has been defined.

## Main Definitions

- `rat` is the structure encoding `ℚ`.

## Notations

## Tags

rat, rationals, field, ℚ, numerator, denominator, num, denom
-/

#print Rat /-
/-- `rat`, or `ℚ`, is the type of rational numbers. It is defined
  as the set of pairs ⟨n, d⟩ of integers such that `d` is positive and `n` and
  `d` are coprime. This representation is preferred to the quotient
  because without periodic reduction, the numerator and denominator can grow
  exponentially (for example, adding 1/2 to itself repeatedly). -/
structure Rat where mk' ::
  num : ℤ
  den : ℕ
  Pos : 0 < denom
  cop : Num.natAbs.coprime denom
#align rat Rat
-/

-- mathport name: exprℚ
notation "ℚ" => Rat

namespace Rat

/-- String representation of a rational numbers, used in `has_repr`, `has_to_string`, and
`has_to_format` instances. -/
protected def repr : ℚ → String
  | ⟨n, d, _, _⟩ => if d = 1 then repr n else repr n ++ "/" ++ repr d
#align rat.repr Rat.repr

instance : Repr ℚ :=
  ⟨Rat.repr⟩

instance : ToString ℚ :=
  ⟨Rat.repr⟩

unsafe instance : has_to_format ℚ :=
  ⟨coe ∘ Rat.repr⟩

#print Rat.ext_iff /-
theorem ext_iff {p q : ℚ} : p = q ↔ p.num = q.num ∧ p.den = q.den :=
  by
  cases p
  cases q
  simp
#align rat.ext_iff Rat.ext_iff
-/

#print Rat.ext /-
@[ext]
theorem ext {p q : ℚ} (hn : p.num = q.num) (hd : p.den = q.den) : p = q :=
  Rat.ext_iff.mpr ⟨hn, hd⟩
#align rat.ext Rat.ext
-/

end Rat


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Yuyang Zhao

! This file was ported from Lean 3 source module ring_theory.polynomial.tower
! leanprover-community/mathlib commit bb168510ef455e9280a152e7f31673cabd3d7496
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Algebra.Tower
import Mathlib.Data.Polynomial.AlgebraMap

/-!
# Algebra towers for polynomial

This file proves some basic results about the algebra tower structure for the type `R[X]`.

This structure itself is provided elsewhere as `Polynomial.isScalarTower`

When you update this file, you can also try to make a corresponding update in
`RingTheory.MvPolynomial.Tower`.
-/

open Polynomial

variable (R A B : Type _)

namespace Polynomial

section Semiring

variable [CommSemiring R] [CommSemiring A] [Semiring B]

variable [Algebra R A] [Algebra A B] [Algebra R B]

variable [IsScalarTower R A B]

variable {R B}

@[simp]
theorem aeval_map_algebraMap (x : B) (p : R[X]) : aeval x (map (algebraMap R A) p) = aeval x p := by
  rw [aeval_def, aeval_def, eval₂_map, IsScalarTower.algebraMap_eq R A B]
#align polynomial.aeval_map_algebra_map Polynomial.aeval_map_algebraMap

end Semiring

section CommSemiring

variable [CommSemiring R] [CommSemiring A] [Semiring B]

variable [Algebra R A] [Algebra A B] [Algebra R B] [IsScalarTower R A B]

variable {R A}

theorem aeval_algebraMap_apply (x : A) (p : R[X]) :
    aeval (algebraMap A B x) p = algebraMap A B (aeval x p) := by
  rw [aeval_def, aeval_def, hom_eval₂, ← IsScalarTower.algebraMap_eq]
#align polynomial.aeval_algebra_map_apply Polynomial.aeval_algebraMap_apply

@[simp]
theorem aeval_algebraMap_eq_zero_iff [NoZeroSMulDivisors A B] [Nontrivial B] (x : A) (p : R[X]) :
    aeval (algebraMap A B x) p = 0 ↔ aeval x p = 0 := by
  rw [aeval_algebraMap_apply, Algebra.algebraMap_eq_smul_one, smul_eq_zero,
    iff_false_intro (one_ne_zero' B), or_false_iff]
#align polynomial.aeval_algebra_map_eq_zero_iff Polynomial.aeval_algebraMap_eq_zero_iff

variable {B}

theorem aeval_algebraMap_eq_zero_iff_of_injective {x : A} {p : R[X]}
    (h : Function.Injective (algebraMap A B)) : aeval (algebraMap A B x) p = 0 ↔ aeval x p = 0 := by
  rw [aeval_algebraMap_apply, ← (algebraMap A B).map_zero, h.eq_iff]
#align polynomial.aeval_algebra_map_eq_zero_iff_of_injective Polynomial.aeval_algebraMap_eq_zero_iff_of_injective

end CommSemiring

end Polynomial

namespace Subalgebra

open Polynomial

section CommSemiring

variable {R A} [CommSemiring R] [CommSemiring A] [Algebra R A]

@[simp]
theorem aeval_coe (S : Subalgebra R A) (x : S) (p : R[X]) : aeval (x : A) p = aeval x p :=
  aeval_algebraMap_apply A x p
#align subalgebra.aeval_coe Subalgebra.aeval_coe

end CommSemiring

end Subalgebra


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import analysis.calculus.parametric_integral
import measure_theory.integral.interval_integral

/-!
# Derivatives of interval integrals depending on parameters

In this file we restate theorems about derivatives of integrals depending on parameters for interval
integrals.  -/

open topological_space measure_theory filter metric
open_locale topological_space filter interval

variables {α 𝕜 : Type*} [measurable_space α] [linear_order α] [topological_space α]
          [order_topology α] [opens_measurable_space α] {μ : measure α} [is_R_or_C 𝕜]
          {E : Type*} [normed_group E] [normed_space ℝ E]
          [normed_space 𝕜 E] [is_scalar_tower ℝ 𝕜 E]
          [complete_space E] [second_countable_topology E]
          [measurable_space E] [borel_space E]
          {H : Type*} [normed_group H] [normed_space 𝕜 H] [second_countable_topology $ H →L[𝕜] E]
          {a b : α} {bound : α → ℝ} {ε : ℝ}

namespace interval_integral

/-- Differentiation under integral of `x ↦ ∫ t in a..b, F x t` at a given point `x₀`, assuming
`F x₀` is integrable, `x ↦ F x a` is locally Lipschitz on a ball around `x₀` for ae `a`
(with a ball radius independent of `a`) with integrable Lipschitz bound, and `F x` is ae-measurable
for `x` in a possibly smaller neighborhood of `x₀`. -/
lemma has_fderiv_at_integral_of_dominated_loc_of_lip {F : H → α → E} {F' : α → (H →L[𝕜] E)} {x₀ : H}
  (ε_pos : 0 < ε)
  (hF_meas : ∀ᶠ x in 𝓝 x₀, ae_measurable (F x) (μ.restrict (Ι a b)))
  (hF_int : interval_integrable (F x₀) μ a b)
  (hF'_meas : ae_measurable F' (μ.restrict (Ι a b)))
  (h_lip : ∀ᵐ t ∂μ, t ∈ Ι a b → lipschitz_on_with (real.nnabs $ bound t) (λ x, F x t) (ball x₀ ε))
  (bound_integrable : interval_integrable bound μ a b)
  (h_diff : ∀ᵐ t ∂μ, t ∈ Ι a b → has_fderiv_at (λ x, F x t) (F' t) x₀) :
  interval_integrable F' μ a b ∧
    has_fderiv_at (λ x, ∫ t in a..b, F x t ∂μ) (∫ t in a..b, F' t ∂μ) x₀ :=
begin
  simp only [interval_integrable_iff, interval_integral_eq_integral_interval_oc,
    ← ae_restrict_iff' measurable_set_interval_oc] at *,
  have := has_fderiv_at_integral_of_dominated_loc_of_lip ε_pos hF_meas hF_int hF'_meas h_lip
    bound_integrable h_diff,
  exact ⟨this.1, this.2.const_smul _⟩
end

/-- Differentiation under integral of `x ↦ ∫ F x a` at a given point `x₀`, assuming
`F x₀` is integrable, `x ↦ F x a` is differentiable on a ball around `x₀` for ae `a` with
derivative norm uniformly bounded by an integrable function (the ball radius is independent of `a`),
and `F x` is ae-measurable for `x` in a possibly smaller neighborhood of `x₀`. -/
lemma has_fderiv_at_integral_of_dominated_of_fderiv_le {F : H → α → E} {F' : H → α → (H →L[𝕜] E)}
  {x₀ : H} (ε_pos : 0 < ε)
  (hF_meas : ∀ᶠ x in 𝓝 x₀, ae_measurable (F x) (μ.restrict (Ι a b)))
  (hF_int : interval_integrable (F x₀) μ a b)
  (hF'_meas : ae_measurable (F' x₀) (μ.restrict (Ι a b)))
  (h_bound : ∀ᵐ t ∂μ, t ∈ Ι a b → ∀ x ∈ ball x₀ ε, ∥F' x t∥ ≤ bound t)
  (bound_integrable : interval_integrable bound μ a b)
  (h_diff : ∀ᵐ t ∂μ, t ∈ Ι a b → ∀ x ∈ ball x₀ ε, has_fderiv_at (λ x, F x t) (F' x t) x) :
  has_fderiv_at (λ x, ∫ t in a..b, F x t ∂μ) (∫ t in a..b, F' x₀ t ∂μ) x₀ :=
begin
  simp only [interval_integrable_iff, interval_integral_eq_integral_interval_oc,
    ← ae_restrict_iff' measurable_set_interval_oc] at *,
  exact (has_fderiv_at_integral_of_dominated_of_fderiv_le ε_pos hF_meas hF_int hF'_meas h_bound
    bound_integrable h_diff).const_smul _
end

/-- Derivative under integral of `x ↦ ∫ F x a` at a given point `x₀ : 𝕜`, `𝕜 = ℝ` or `𝕜 = ℂ`,
assuming `F x₀` is integrable, `x ↦ F x a` is locally Lipschitz on a ball around `x₀` for ae `a`
(with ball radius independent of `a`) with integrable Lipschitz bound, and `F x` is
ae-measurable for `x` in a possibly smaller neighborhood of `x₀`. -/
lemma has_deriv_at_integral_of_dominated_loc_of_lip {F : 𝕜 → α → E} {F' : α → E} {x₀ : 𝕜}
  (ε_pos : 0 < ε)
  (hF_meas : ∀ᶠ x in 𝓝 x₀, ae_measurable (F x) (μ.restrict (Ι a b)))
  (hF_int : interval_integrable (F x₀) μ a b)
  (hF'_meas : ae_measurable F' (μ.restrict (Ι a b)))
  (h_lipsch : ∀ᵐ t ∂μ, t ∈ Ι a b →
    lipschitz_on_with (real.nnabs $ bound t) (λ x, F x t) (ball x₀ ε))
  (bound_integrable : interval_integrable (bound : α → ℝ) μ a b)
  (h_diff : ∀ᵐ t ∂μ, t ∈ Ι a b → has_deriv_at (λ x, F x t) (F' t) x₀) :
  (interval_integrable F' μ a b) ∧
    has_deriv_at (λ x, ∫ t in a..b, F x t ∂μ) (∫ t in a..b, F' t ∂μ) x₀ :=
begin
  simp only [interval_integrable_iff, interval_integral_eq_integral_interval_oc,
    ← ae_restrict_iff' measurable_set_interval_oc] at *,
  have := has_deriv_at_integral_of_dominated_loc_of_lip ε_pos hF_meas hF_int hF'_meas h_lipsch
    bound_integrable h_diff,
  exact ⟨this.1, this.2.const_smul _⟩
end

/-- Derivative under integral of `x ↦ ∫ F x a` at a given point `x₀ : 𝕜`, `𝕜 = ℝ` or `𝕜 = ℂ`,
assuming `F x₀` is integrable, `x ↦ F x a` is differentiable on an interval around `x₀` for ae `a`
(with interval radius independent of `a`) with derivative uniformly bounded by an integrable
function, and `F x` is ae-measurable for `x` in a possibly smaller neighborhood of `x₀`. -/
lemma has_deriv_at_integral_of_dominated_loc_of_deriv_le {F : 𝕜 → α → E} {F' : 𝕜 → α → E} {x₀ : 𝕜}
  (ε_pos : 0 < ε)
  (hF_meas : ∀ᶠ x in 𝓝 x₀, ae_measurable (F x) (μ.restrict (Ι a b)))
  (hF_int : interval_integrable (F x₀) μ a b)
  (hF'_meas : ae_measurable (F' x₀) (μ.restrict (Ι a b)))
  (h_bound : ∀ᵐ t ∂μ, t ∈ Ι a b → ∀ x ∈ ball x₀ ε, ∥F' x t∥ ≤ bound t)
  (bound_integrable : interval_integrable bound μ a b)
  (h_diff : ∀ᵐ t ∂μ, t ∈ Ι a b → ∀ x ∈ ball x₀ ε, has_deriv_at (λ x, F x t) (F' x t) x) :
  (interval_integrable (F' x₀) μ a b) ∧
    has_deriv_at (λ x, ∫ t in a..b, F x t ∂μ) (∫ t in a..b, F' x₀ t ∂μ) x₀ :=
begin
  simp only [interval_integrable_iff, interval_integral_eq_integral_interval_oc,
    ← ae_restrict_iff' measurable_set_interval_oc] at *,
  have := has_deriv_at_integral_of_dominated_loc_of_deriv_le ε_pos hF_meas hF_int hF'_meas h_bound
    bound_integrable h_diff,
  exact ⟨this.1, this.2.const_smul _⟩
end

end interval_integral


/-

# Function world.

If you have beaten Addition World, then you have got
quite good at manipulating equalities in Lean using the `rw` tactic.
But there are plenty of levels later on which will require you
to manipulate functions, and `rw` is not the tool for you here.

To manipulate functions effectively, we need to learn about a new collection
of tactics, namely `exact`, `intro`, `have` and `apply`. These tactics
are specially designed for dealing with functions. Of course we are
ultimately interested in using these tactics to prove theorems
about the natural numbers – but in this
world there is little point in working with specific sets like `mynat`,
everything works for general sets.

So our notation for this level is: $P$, $Q$, $R$ and so on denote general sets,
and $h$, $j$, $k$ and so on denote general
functions between them. What we will learn in this world is how to use functions
in Lean to push elements from set to set. A word of warning –
even though there's no harm at all in thinking of $P$ being a set and $p$
being an element, you will not see Lean using the notation $p\in P$, because
internally Lean stores $P$ as a "Type" and $p$ as a "term", and it uses `p : P`
to mean "$p$ is a term of type $P$", Lean's way of expressing the idea that $p$
is an element of the set $P$. You have seen this already – Lean has
been writing `n : mynat` to mean that $n$ is a natural number.

## A new kind of goal.

All through addition world, our goals have been theorems,
and it was our job to find the proofs.
**The levels in function world aren't theorems**. This is the only world where
the levels aren't theorems in fact. In function world the object of a level
is to create an element of the set in the goal. The goal will look like `⊢ X`
with $X$ a set and you get rid of the goal by constructing an element of $X$.
I don't know if you noticed this, but you finished
essentially every goal of Addition World (and Multiplication World and Power World,
if you played them) with `refl`.
This tactic is no use to us here.
We are going to have to learn a new way of solving goals – the `exact` tactic.

If you delete the sorry below then your local context will look like this:

```
P Q : Type,
p : P,
h : P → Q
⊢ Q
```

In this situation, we have sets $P$ and $Q$ (but Lean calls them types),
and an element $p$ of $P$ (written `p : P`
but meaning $p\in P$). We also have a function $h$ from $P$ to $Q$,
and our goal is to construct an
element of the set $Q$. It's clear what to do *mathematically* to solve
this goal -- we can
make an element of $Q$ by applying the function $h$ to
the element $p$. But how to do it in Lean? There are at least two ways
to explain this idea to Lean,
and here we will learn about one of them, namely the method which
uses the `exact` tactic.

## The `exact` tactic.

If you can explicitly see how to make an element of your goal set,
i.e. you have a formula for it, then you can just write `exact <formula>`
and this will close the goal.

### Example

If your local context looks like this

```
P Q : Type,
p : P,
h : P → Q
⊢ Q
```

then $h(p)$ is an element of $Q$ so you can just write

`exact h(p),`

to close the goal.

## Important note

Note that `exact h(P),` won't work (with a capital $P$);
this is a common error I see from beginners.
$P$ is not an element of $P$, it's $p$ that is an element of $P$.

## Level 1: the `exact` tactic.
-/

/- Definition
Given an element of $P$ and a function from $P$ to $Q$,
we define an element of $Q$.
-/
example (P Q : Type) (p : P) (h : P → Q) : Q :=
begin
exact h(p),

end

/- Tactic : exact

## Summary

If the goal is `⊢ X` then `exact x` will close the goal if
and only if `x` is a term of type `X`.

## Details

Say $P$, $Q$ and $R$ are types (i.e., what a mathematician
might think of as either sets or propositions),
and the local context looks like this:

```
p : P,
h : P → Q,
j : Q → R
⊢ R
```

If you can spot how to make a term of type `R`, then you
can just make it and say you're done using the `exact` tactic
together with the formula you have spotted. For example the
above goal could be solved with

`exact j(h(p)),`

because $j(h(p))$ is easily checked to be a term of type $R$
(i.e., an element of the set $R$, or a proof of the proposition $R$).

-/


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad
-/

import Mathlib.Init.Algebra.Order
import Mathlib.Init.Data.Int.Basic

/-! # The order relation on the integers -/

open Nat

namespace Int

#align int.nonneg Int.NonNeg
#align int.le Int.le
#align int.lt Int.lt

export private decNonneg from Init.Data.Int.Basic
#align int.decidable_nonneg Int.decNonneg
#align int.decidable_le Int.decLe
#align int.decidable_lt Int.decLt

theorem le.elim {a b : ℤ} (h : a ≤ b) {P : Prop} (h' : ∀ n : ℕ, a + ↑n = b → P) : P :=
  Exists.elim (le.dest h) h'
#align int.le.elim Int.le.elim

alias ofNat_le ↔ le_of_ofNat_le_ofNat ofNat_le_ofNat_of_le
#align int.coe_nat_le_coe_nat_of_le Int.ofNat_le_ofNat_of_le
#align int.le_of_coe_nat_le_coe_nat Int.le_of_ofNat_le_ofNat
#align int.coe_nat_le_coe_nat_iff Int.ofNat_le

#align int.coe_zero_le Int.ofNat_zero_le
#align int.eq_coe_of_zero_le Int.eq_ofNat_of_zero_le

theorem lt.elim {a b : ℤ} (h : a < b) {P : Prop} (h' : ∀ n : ℕ, a + ↑(Nat.succ n) = b → P) : P :=
  Exists.elim (lt.dest h) h'
#align int.lt.elim Int.lt.elim

alias ofNat_lt ↔ lt_of_ofNat_lt_ofNat ofNat_lt_ofNat_of_lt
#align int.coe_nat_lt_coe_nat_iff Int.ofNat_lt
#align int.lt_of_coe_nat_lt_coe_nat Int.lt_of_ofNat_lt_ofNat
#align int.coe_nat_lt_coe_nat_of_lt Int.ofNat_lt_ofNat_of_lt

instance : LinearOrder ℤ where
  le := (·≤·)
  le_refl := Int.le_refl
  le_trans := @Int.le_trans
  le_antisymm := @Int.le_antisymm
  lt := (·<·)
  lt_iff_le_not_le := @Int.lt_iff_le_not_le
  le_total := Int.le_total
  decidable_eq := by infer_instance
  decidable_le := by infer_instance
  decidable_lt := by infer_instance

#align int.eq_nat_abs_of_zero_le Int.eq_natAbs_of_zero_le
#align int.le_nat_abs Int.le_natAbs
#align int.neg_succ_lt_zero Int.negSucc_lt_zero
#align int.eq_neg_succ_of_lt_zero Int.eq_negSucc_of_lt_zero
#align int.sub_eq_zero_iff_eq Int.sub_eq_zero

theorem neg_mul_eq_neg_mul_symm (a b : ℤ) : -a * b = -(a * b) := (Int.neg_mul_eq_neg_mul a b).symm
#align int.neg_mul_eq_neg_mul_symm Int.neg_mul_eq_neg_mul_symm

theorem mul_neg_eq_neg_mul_symm (a b : ℤ) : a * -b = -(a * b) := (Int.neg_mul_eq_mul_neg a b).symm
#align int.mul_neg_eq_neg_mul_symm Int.mul_neg_eq_neg_mul_symm

#align int.of_nat_nonneg Int.ofNat_nonneg
#align int.coe_succ_pos Int.ofNat_succ_pos
#align int.exists_eq_neg_of_nat Int.exists_eq_neg_ofNat
#align int.nat_abs_of_nonneg Int.natAbs_of_nonneg
#align int.of_nat_nat_abs_of_nonpos Int.ofNat_natAbs_of_nonpos

protected


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

! This file was ported from Lean 3 source module data.fintype.parity
! leanprover-community/mathlib commit 509de852e1de55e1efa8eacfa11df0823f26f226
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Fintype.Card
import Mathlib.Algebra.Parity

/-!
# The cardinality of `Fin (bit0 n)` is even.
-/

variable {α : Type _}

namespace Fintype

instance IsSquare.decidablePred [Mul α] [Fintype α] [DecidableEq α] :
    DecidablePred (IsSquare : α → Prop) := fun _ => Fintype.decidableExistsFintype
#align fintype.is_square.decidable_pred Fintype.IsSquare.decidablePred

end Fintype

set_option linter.deprecated false

/-- The cardinality of `Fin (bit0 n)` is even, `fact` version.
This `fact` is needed as an instance by `Matrix.SpecialLinearGroup.has_neg`. -/
theorem Fintype.card_fin_even {n : ℕ} : Fact (Even (Fintype.card (Fin (bit0 n)))) :=
  ⟨by rw [Fintype.card_fin]; exact even_bit0 _⟩
#align fintype.card_fin_even Fintype.card_fin_even