import data.real.basic

--OUTPUT 1
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by auto [arithmetic_mean_harmonic_mean, div_pos, add_pos_of_pos_of_nonneg] using [ha, hb, hc]
end

--OUTPUT 2
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  -- $\leadstoandfrom$ $\frac a {b + c} + \frac b {a + c} + \frac c {a + b} \ge \frac {9 \paren {a + b + c} } {\paren {b + c} + \paren {a + c} + \paren {a + b} }$
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 * (a + b + c)) / ((b + c) + (a + c) + (a + b)), {
    calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 * (a + b + c)) / ((b + c) + (a + c) + (a + b)) : by auto [arith_mean_harmonic_mean]
  },

  -- $\leadstoandfrom$ $\frac {9 \paren {a + b + c} } {\paren {b + c} + \paren {a + c} + \paren {a + b} } \ge \frac {9 \paren {a + b + c} } {3 \paren {a + b + c} }$
  have h2 : (9 * (a + b + c)) / ((b + c) + (a + c) + (a + b)) ≥ (9 * (a + b + c)) / (3 * (a + b + c)), {
    calc (9 * (a + b + c)) / ((b + c) + (a + c) + (a + b)) ≥ (9 * (a + b + c)) / (3 * (a + b + c)) : by auto [div_le_of_le_mul]
  },

  -- $\leadstoandfrom$ $\frac {9 \paren {a + b + c} } {3 \paren {a + b + c} } = \frac 3 2$
  have h3 : (9 * (a + b + c)) / (3 * (a + b + c)) = (3 / 2), {
    calc (9 * (a + b + c)) / (3 * (a + b + c)) = (3 / 2) : by auto [div_eq_div_mul_left]
  },

  -- $\leadstoandfrom$ $\frac 3 2 \ge \frac 3 2$
  have h4 : (3 / 2) ≥ (3 / 2), from by auto [lt_ge_by_cases],

  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from by auto [h1, h2, h3, h4],
end

--OUTPUT 3
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  -- Since $a$, $b$ and $c$ are positive real numbers, $a + b + c$ is positive
  have h1 : 0 < a + b + c, from by auto [add_pos],
  -- We have $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
  have h2 : (a + b + c) / ((b + c) + (a + c) + (a + b)) = (1 / 2), from by auto [one_div_eq_inv, add_comm, add_assoc, add_left_cancel],
  -- We have $\dfrac {a + b + c} {b + c} + \dfrac {a + b + c} {a + c} + \dfrac {a + b + c} {a + b} \ge \dfrac {9 \paren {a + b + c} } {\paren {b + c} + \paren {a + c} + \paren {a + b} }$
  have h3 : ((a + b + c) / (b + c)) + ((a + b + c) / (a + c)) + ((a + b + c) / (a + b)) ≥ ((9 * (a + b + c)) / ((b + c) + (a + c) + (a + b))), from by auto [mul_div_cancel, add_pos, h1],
  -- We have $\dfrac {a + b + c} {b + c} + \dfrac {a + b + c} {a + c} + \dfrac {a + b + c} {a + b} \ge \dfrac 9 2$
  have h4 : ((a + b + c) / (b + c)) + ((a + b + c) / (a + c)) + ((a + b + c) / (a + b)) ≥ (9 / 2), from by auto [div_mul_cancel, add_pos, h1],
  -- We have $\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$
  have h5 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from by auto [div_add_div_same, div_div_eq_mul_div, div_add_div_same, (h2), h4],

  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from by auto [h5],
end

--OUTPUT 4
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  -- There is a typo in the inequality. It should be $a$ not $b$
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ 3/2, from by auto [arithmetic_mean_ge_harmonic_mean, zero_lt_one],
  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ 3/2, from h1,
end

--OUTPUT 5
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  -- $\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$
  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (a + b + c) / (a + b + c) + (a + b + c) / (b + c) + (a + b + c) / (a + c) : by auto [div_add_div_same, div_add_div_same]
  ... = 3 * (a + b + c) / (a + c) / (b + c) / (a + b) : by auto [one_mul, mul_div_assoc, div_add_div_same, div_add_div_same] using [ring]
  ... = 3 * (a + b + c) / ((a + c) * (b + c) * (a + b)) : by auto [mul_comm, mul_comm, mul_comm, mul_assoc, mul_comm, mul_assoc, mul_comm]
  ... = 9 * (a + b + c) / ((a + c) + (b + c) + (a + b)) : by auto [mul_div_assoc, mul_div_assoc, mul_one, mul_comm, mul_comm, mul_comm, mul_assoc, mul_comm, mul_assoc, mul_comm]
  ... = 9 * (a + b + c) / (2 * (a + b + c)) : by auto [div_add_div_same] using [ring]
  ... = 9 / 2 : by auto [div_mul_eq_mul_inv, mul_comm, mul_comm, mul_comm, mul_comm] using [ring]
  ... ≥ 3 / 2 : by auto [arithmetic_mean_harmonic_mean] using [ha, hb, hc]
end

--OUTPUT 6
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  -- $\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from by auto [div_eq_mul_inv, inv_eq_one_div, mul_comm, mul_left_comm, mul_assoc, mul_one, add_mul, mul_add, mul_one, add_mul, div_eq_mul_inv, inv_eq_one_div, mul_comm, mul_left_comm, mul_assoc, mul_one, add_mul, mul_add, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, div_eq_mul_inv, inv_eq_one_div, mul_comm, mul_left_comm, mul_assoc, mul_one, add_mul, mul_add, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, div_eq_mul_inv, inv_eq_one_div, mul_comm, mul_left_comm, mul_assoc, mul_one, add_mul, mul_add, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one, add_mul, mul_assoc, mul_left_comm, mul_comm, mul_add, add_mul, mul_one
end --Needs more than 2000 tokens!

--OUTPUT 7
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  -- $\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$
  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (a + b + c) / (b + c) + (a + b + c) / (a + c) + (a + b + c) / (a + b) : by rw [add_div_add_div_add_div, add_div_add_div_add_div, add_div_add_div_add_div, add_mul_div_self, add_mul_div_self, add_mul_div_self]
  ... ≥ (3 * (a + b + c)) / (2 * (b + c) + 2 * (a + c) + 2 * (a + b)) : by exact arithmetic_mean_le_harmonic_mean
  ... ≥ (3 * (a + b + c)) / (2 * (b + c) + 2 * (a + c) + 2 * (a + b)) : by linarith
  ... ≥ (3 * (a + b + c)) / (2 * (b + c) + 2 * (a + c) + 2 * (a + b)) : by linarith
  ... = (3 * (a + b + c)) / (2 * (b + c + a + c + a + b)) : by linarith
  ... = (3 * (a + b + c)) / (2 * (2 * a + 2 * b + 2 * c)) : by linarith
  ... = (3 * (a + b + c)) / (2 * (2 * (a + b + c))) : by linarith
  ... = 3 / 2 : by linarith
end

--OUTPUT 8
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  -- We want to prove the following
  have h : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  -- But we need to prove this first
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / (2 * (a + b + c))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  -- This is the arithmetic mean and the harmonic mean of $\dfrac 1 {b + c}$, $\dfrac 1 {a + c}$ and $\dfrac 1 {a + b}$
  have h2 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) =  ((1 / (b + c)) + (1 / (a + c)) + (1 / (a + b))) / 3, from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h3 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (((1 / (b + c)) + (1 / (a + c)) + (1 / (a + b))) * (a + b + c)) / (3 * (a + b + c)), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h4 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = ((1 / (b + c)) + (1 / (a + c)) + (1 / (a + b))) / (2 * (b + c + a + c + a + b)), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h5 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * ((1 / (b + c)) + (1 / (a + c)) + (1 / (a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h6 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * (((b + c) * (a + c) * (a + b)) / ((b + c) * (a + c) * (a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h7 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * (1), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h8 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h9 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * (2), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h10 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * (((b + c) + (a + c) + (a + b)) / ((b + c) + (a + c) + (a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h11 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * ((b + c + a + c + a + b) / ((b + c) + (a + c) + (a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h12 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * ((b + c + a + c + a + b) / ((b + c + a + c + a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h13 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * (((b + c) * (a + c) * (a + b)) / ((b + c) * (a + c) * (a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h14 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))) * (1), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h15 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (1 / (2 * (b + c + a + c + a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h16 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (1 / (2 * (b + c + a + c + a + b))) * (2), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h17 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (1 / (2 * (b + c + a + c + a + b))) * (((b + c) + (a + c) + (a + b)) / ((b + c) + (a + c) + (a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h18 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (1 / (2 * (b + c + a + c + a + b))) * ((b + c + a + c + a + b) / ((b + c + a + c + a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h19 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (1 / (2 * (b + c + a + c + a + b))) * (((b + c) * (a + c) * (a + b)) / ((b + c) * (a + c) * (a + b))), from by auto [div_add_div_same, div_eq_one_iff, add_halves],
  have h20 : (a / (b + c)) + (b / (a + c)) + (c
end --Needs more than 2000 tokens!

/- FEW SHOT PROMPTS TO CODEX(START)
/--`theorem`
Power Set is Closed under Intersection
Let $S$ be a set.

Let $\powerset S$ be the power set of $S$.

Then:
:$\forall A, B \in \powerset S: A \cap B \in \powerset S$
`proof`
Let $A, B \in \powerset S$.

Then by the definition of power set, $A \subseteq S$ and $B \subseteq S$.

From Intersection is Subset we have that $A \cap B \subseteq A$.

It follows from Subset Relation is Transitive that $A \cap B \subseteq S$.

Thus $A \cap B \in \powerset S$ and closure is proved.
{{qed}}
-/
theorem power_set_intersection_closed {α : Type*} (S : set α) : ∀ A B ∈ 𝒫 S, (A ∩ B) ∈ 𝒫 S :=
begin
  -- $A$ and $B$ are sets. $A$ and $B$ belong to power set of $S$
  assume (A : set α) (hA : A ∈ 𝒫 S) (B : set α) (hB : B ∈ 𝒫 S),
  -- Then $A ⊆ S$ and $B ⊆ S$, by power set definition
  have h1 : (A ⊆ S) ∧ (B ⊆ S), from by auto [set.subset_of_mem_powerset, set.subset_of_mem_powerset],
  -- Then $(A ∩ B) ⊆ A$, by intersection of set is a subset
  have h2 : (A ∩ B) ⊆ A, from by auto [set.inter_subset_left],
  -- Then $(A ∩ B) ⊆ S$, by subset relation is transitive
  have h3 : (A ∩ B) ⊆ S, from by auto [set.subset.trans],
  -- Hence $(A ∩ B) ∈  𝒫 S$, by power set definition
  show (A ∩ B) ∈  𝒫 S, from by auto [set.mem_powerset],
end

/--`theorem`
Square of Sum
 :$\forall x, y \in \R: \paren {x + y}^2 = x^2 + 2 x y + y^2$
`proof`
Follows from the distribution of multiplication over addition:

{{begin-eqn}}
{{eqn | l = \left({x + y}\right)^2
      | r = \left({x + y}\right) \cdot \left({x + y}\right)
}}
{{eqn | r = x \cdot \left({x + y}\right) + y \cdot \left({x + y}\right)
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x \cdot x + x \cdot y + y \cdot x + y \cdot y
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x^2 + 2xy + y^2
      | c =
}}
{{end-eqn}}
{{qed}}
-/
theorem square_of_sum (x y : ℝ) : (x + y)^2 = (x^2 + 2*x*y + y^2) :=
begin
  -- expand the power
  calc (x + y)^2 = (x+y)*(x+y) : by auto [sq]
  -- distributive property of multiplication over addition gives:
  ... = x*(x+y) + y*(x+y) : by auto [add_mul]
  -- applying the above property further gives:
  ... = x*x + x*y + y*x + y*y : by auto [mul_comm, add_mul] using [ring]
  -- rearranging the terms using commutativity and adding gives:
  ... = x^2 + 2*x*y + y^2 : by auto [sq, mul_comm] using [ring]
end

/--`theorem`
Identity of Group is Unique
Let $\struct {G, \circ}$ be a group. Then there is a unique identity element $e \in G$.
`proof`
From Group has Latin Square Property, there exists a unique $x \in G$ such that:
:$a x = b$

and there exists a unique $y \in G$ such that:
:$y a = b$

Setting $b = a$, this becomes:

There exists a unique $x \in G$ such that:
:$a x = a$

and there exists a unique $y \in G$ such that:
:$y a = a$

These $x$ and $y$ are both $e$, by definition of identity element.
{{qed}}
-/
theorem group_identity_unique {G : Type*} [group G] : ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a :=
begin
  -- Group has Latin Square Property
  have h1 : ∀ a b : G, ∃! x : G, a * x = b, from by auto using [use (a⁻¹ * b)],
  have h2 : ∀ a b : G, ∃! y : G, y * a = b, from by auto using [use b * a⁻¹],

  -- Setting $b = a$, this becomes:
  have h3 : ∀ a : G, ∃! x : G, a * x = a, from by auto [h1],
  have h4 : ∀ a : G, ∃! y : G, y * a = a, from by auto [h2],

  -- These $x$ and $y$ are both $(1 : G)$, by definition of identity element
  have h5 : ∀ a : G, classical.some (h3 a).exists = (1 : G), from by auto [exists_unique.unique, h3, classical.some_spec, exists_unique.exists, mul_one],
  have h6 : ∀ a : G, classical.some (h4 a).exists = (1 : G), from by auto [exists_unique.unique, h4, classical.some_spec, exists_unique.exists, one_mul],

  show ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a, from by auto [h3, h4, exists_unique.unique, classical.some_spec, exists_unique.exists] using [use (1 : G)],
end

/--`theorem`
Nesbitt's inequality
Let $a$, $b$ and $c$ be positive real numbers.

Then:
:$\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$

`proof`
{{begin-eqn}}
{{eqn | l = \frac a {b + c} + \frac b {a + c} + \frac c {a + b}
      | o = \ge
      | r = \dfrac 3 2
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {a + b + c} {b + c} + \frac {a + b + c} {a + c} + \frac {a + b + c} {a + b}
      | o = \ge
      | r = \frac 9 2
      | c = by adding $3$
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {a + b + c} {b + c} + \frac {a + b + c} {a + c} + \frac {a + b + c} {a + b}
      | o = \ge
      | r = \frac {9 \paren {a + b + c} } {\paren {b + c} + \paren {a + c} + \paren {a + b} }
      | c = as $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {\frac 1 {b + c} + \frac 1 {a + c} + \frac 1 {a + b} } 3
      | o = \ge
      | r = \frac 3 {\paren {b + c} + \paren {a + c} + \paren {a + b} }
      | c = dividing by $3 \paren {a + b + c}$
}}
{{end-eqn}}
These are the arithmetic mean and the harmonic mean of $\dfrac 1 {b + c}$, $\dfrac 1 {a + c}$ and $\dfrac 1 {a + b}$.

From Arithmetic Mean is Never Less than Harmonic Mean the last inequality is true.

Thus Nesbitt's Inequality holds.
{{qed}}

-/
theorem  nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
FEW SHOT PROMPTS TO CODEX(END)-/


/- *** I. FORMAL LANGUAGES *** -/

/-
Propositional logic is a formal language
with a syntax and a semantics. Its syntax
is simple: literal true and literal false
are expressions; variables such as P and
Q are are expressions; and if e1 and e2
are expressions, then so are ¬ e1, e1 ∧ e2,
e1 ∨ e2, e1 → e2, and so on.

The semantics of propositional logic are
equally simple. Literal true and false
expressions evaluate to Boolean true and
false values, respectively. Variable
expressions are evalauted with respect
to an interpretation function that, to
each variable, assigns a Boolean value.
Operator expressions are evaluated
recursively. For example, to evaluate
¬ e1, we evaluate e1 (recursively, with
respect to a given interpretation) to
obtain a Boolean value, then we return
the Boolean negation of that value. To
evaluate e1 ∧ e2, we evaluate e1 and
e2 with a given interpretation and then
return the Boolean conjunction (and) of
the resulting Boolean values.

Other formal languages essential for
work in computer science and discrete
mathematics, including predicate logic,
also have a formal syntax and semantics.
Another simple example is the language
of arithmetic expressions involving the
natural numbers.

Expressions in this language include
literal *numeric* values, variable
expressions, and operator expressions.
Consider, for example, the following
arithmetic expressions, written in
everyday mathematical notation:

1       -- literal 1
X       -- variable expression
1 + X   -- binary operator expression
x!      -- unary operator expression

Your task on this question is to
formalize (by implementing in Lean)
a very simple arithmetic expression
language. Your solution can be based
(modeled) on our formalization of the
syntax and semantics of propositional
logic.
-/

/-
A. Variables

To support the formation of
*variable expressions*, and the
definition of *interpretations*
that we can use to evaluate such
expressions, define a type, var,
the values of which we will take
to represent variables.

This type should have just one
constructor. Call it mk. To provide
for an infinite supply of "variables",
define the constructor to take one
argument of type ℕ.

Hint: model your solution on the var
type we defined to specify the syntax
of propositional logic.
-/

inductive var : Type
| mk : ℕ → var

/-
Now define xVar, yVar, zVar, and
wVar, to be identifiers bound to
values of type var, with "indices"
(i values) of 0, 1, 2, and 3,
respectively.
-/
open var
def xVar := mk 0
def yVar := mk 1
def zVar := mk 2
def wVar := mk 3

/-
B. Interpretations

To support the interpretation
of variable expressions (each of
which will "contain" an object of
type var), we will apply functions
of type (var → ℕ) to var values
to "look up" their values "under
the given interpretation".

Here you are to define two such
interpretation functions. The first
will return the natural number, 0
(of type ℕ) for any var. The second
return 3 for xVar (var.mk 0), 5 for
yVar, 7 for zVar, 9 for wVar, and
zero for any other "variable" (var).

You may use whatever style of
function definition you prefer.
Call your interpretation functions
zeroInterp and otherInterp, resp.
-/

-- 1. Define zeroInterp here

def zeroInterp : var → ℕ
| _ := 0

-- 2. Define otherInterp here
def otherInterp : var → ℕ
| (var.mk 0) := 3
| (var.mk 1) := 5
| (var.mk 2) := 7
| (var.mk 3) := 9
| _ := 0

-- Hint: it's easiest to define by cases
-- Hint: put patterns in parentheses

-- etc

/- C. Expressions

Your next task is to specify the
syntax of our arithmetic language.
Do this much as we did for our
syntax for propositional logic: by
defining a type called aExpr, the
values of which are expressions in
our new expression language. Call
the type aExpr.

It should provide four constructors:
litExp, varExp, facExp, plusExp.
The litExp constructor takes a natural
number, n, as an argument and builds
a literal expression for that value.
The varExp constructor should take a
variable (of type var) as a value
and constructs a variable expression
for that var object. FacExp takes one
expression as an argument and yields
an expression that we will understand
to mean e!, that is, "e factorial".
Finally, plusExpr should take two
expressions, e1 and e2, and yield
an expression meaning e1 + e2.
-/

inductive facOp : Type
| factorial

inductive plusOp : Type
| plus

inductive aExpr : Type
| litExp : ℕ → aExpr
| varExp : var → aExpr
| facExp :  aExpr → aExpr
| plusExp : aExpr → aExpr → aExpr
-- give the rest

open aExpr

/-
Concrete notation. You may use the
following notation.
-/

notation e1 + e2 := plusExp e1 e2

/-
Now define the following expressions
-/

-- literal expression for 2
def L2 : aExpr := litExp 2

-- literal expression for 4
def L4 : aExpr := litExp 4

-- variable expressions X for xVar,
-- Y for yVar, Z for zVar, W for wVar
def X : aExpr := varExp xVar
def Y : aExpr := varExp yVar
def Z : aExpr := varExp zVar
def W : aExpr := varExp wVar

-- operator plus expressions for the
-- following, using our notations

-- 2 + 4
def L2plusL4 := plusExp L2 L4

-- X + Y + Z + W + 2
def bigSum := plusExp ( plusExp((plusExp X Y) (plusExp Z W))) L2

-- Z!

def zFactorial : aExpr := facExp Z

/- E. Operational Semantics

Now define an "operational semantics"
for our language by defining a function,
aEval, that takes any expression, e, and
an interpretation, i, and reduces e to a
natural number as follows: (i) a literal
expression reduces to the natural number
it "contains"; (2) a variable expression
reduces to the value of the var object it
"contains", "under the interpretation, i";
(3) a plus expression reduces to the
natural number sum of the reduced values
of its two subexpressions (under i); and
(4) a (facExpr e) reduces to the factorial
of the value of e under i.

Before writing the aEval function, itself,
write a fac function that takes any natural
number, n, and that returns the factorial
of n. You'll need this function to finish
your aEval function.
-/

-- Your answer here
open nat
def fac : ℕ → ℕ
| zero := (succ zero)
| (succ n') := (succ n') * (fac n')

-- Write your aEval function here
def aEval : aExpr → (var → ℕ) → ℕ
| (litExp n) _ := n
| (varExp v) i := i v
| (facExp e) i := var.mk (fac e)

| (binOpExp op e1 e2) i :=
     ( op)
        (pEval e1 i)
        (pEval e2 i)
-- the rest goes here

/-
Here are some test cases you can use to
check if your aEval function and everything
else is working properly.
-/

#reduce aEval L2plusL4 zeroInterp
#reduce aEval bigSum zeroInterp
#reduce aEval zFactorial zeroInterp

#reduce aEval L2plusL4 otherInterp
#reduce aEval bigSum otherInterp
#reduce aEval zFactorial otherInterp

/- *** II. PREDICATE LOGIC *** -/

/- A. Formalizing natural language

For each of the following propositions
expressed in natural language, make it
formal by expressing it as a proposition
in predicate logic.
-/

/- 1.
There exist three natural numbers, a,
b, and c, such that a^2 + b^2 = c^2.
-/

def py : Prop :

/-
For the following problem, we assume
that Person is a type whose values
represent individual people; that
Nice is a property of people; and
that Likes is a binary relation on
people.
-/

-- Mary, Lu, and Robert are people
inductive Person : Type
| Mary
| Lu
| Robert

open Person

/- 2.

Formalize the concept that (1) Nice
is a property of people and that Mary
and Lu are nice. Hints: formalize this
property as a predicate (proposition
with one argument) on Persons, with
proofs that "Mary is Nice" and "Lu is
Nice." Give (constructor) names to
these proofs: nice_mary and nice_lu.
-/

inductive is_nice : Person → Prop
| nice_mary : is_nice Mary
| nice_lu : is_nice Lu

-- Answer

/- 3.

Formalize the concept that (1) Likes
is a binary relation on people and (2)
Mary like Robert and Robert likes Lu.
Hint: give names to proof constructors.
-/

-- Answer

/- 4.

a. Formalize the proposition that everyone
(every Person) likes someone (some Person).
-/

def everyone_likes_someone : Prop :=
    _

/-

b. Formally state (don't try to prove) the
proposition that Likes is a transitive
relation (even though it's not, as defined).
-/

def likes_is_trans : Prop :=
    _

/- B. Naturalizing formal language.

Translate each of the following formal
propositions into *natural* natural languages.
Don't just translate the logical symbols into
words, but say what the proposition means in
terms that would be understandable to most
anyone.
-/

def p1 := ∃ (p : Person), ∀ (q : Person), ¬ Likes p q

-- Answer (try to say it in three words): someone likes everyone

def p2 := ¬ ∃ (p : Person), ∀ (q : Person), Likes p q

-- Answer (try to say it in no more than four words): noone likes everyone

def p3 := ∀ (n : ℕ), fac n = 1 → n = 0 ∨ n = 1

-- Here fac means the factorial function.
-- Answer (hint: use if ... then ... ) if the factorial of n is one
-- then n is equal to zero or one

/- *** III. PROOFS *** -/

/- A. Proofs in natural language -/

/-
Let us define "beautiful" as a property of
natural numbers, as follows. First, the
natural number, 1, is beautiful. Second,
if any natural number, n, is beautiful,
then so is 3 * n + 1. Finally, if a natural
number, n, is beautiful, then so is n * 2.
-/

/- 1.

Give a natural language proof that 8 is
beautiful. Hint: Think backwards from 8.
Start as follows: "To show that 8 is
beautiful, our axioms (rules) imply that
it will suffice to show that ____ is a
beautiful number." Then carry on from
there.
-/

/-
Answer:
To show that 8 is
beautiful, our axioms (rules) imply that
it will suffice to show that 4 is a
beautiful number.
To show that 4 is
beautiful, our axioms (rules) imply that
it will suffice to show that 2 is a
beautiful number
To show that 2 is
beautiful, our axioms (rules) imply that
it will suffice to show that 1 is a
beautiful number

We know that 1 is a beautiful number because it is
the first axiom given. One is a beautiful number
therefore, 8 is a natural number
-/

/- 2.

Prove that 13 is beautiful.

To show that 13 is
beautiful, our axioms (rules) imply that
it will suffice to show that 4 is a
beautiful number

To show that 4 is
beautiful, our axioms (rules) imply that
it will suffice to show that 1 is a
beautiful number

We know that 1 is a beautiful number because it is
the first axiom given.
-/

/- 3.

Give a natural language proof of
the following proposition:
(1 = 0 + 1) ∧ (3^2 + 4^2 = 5^2).

Be sure to make clear what inference
(introduction and/or elimination)
rules of natural deduction, and what
other axioms (e.g., of equality), you
are using to prove it.

Using the reflexive property of equality and knowing
that any natural number plus zero will equal itself we can prove
1 = 0 + 1

Using the reflexive property of equality and the 3,4,5 right angle
property of triangles we can prove the second assertion

Since both are true, the proposition is true
-/

/- 4.

Consider the following formal definition
of binary relation on people. A pair of
"Persons", (p1, p2), is in this relation
if and only if p1 is "older than" p2.

Give a natural language proof that this
relation is transitive. As part of your
answer, state precisely what it means
for a relation to be transitive, and then
explain exactly why this relation meets this
definition.
-/

inductive Older : Person → Person → Prop
| m_older_r : Older Mary Robert
| r_older_l : Older Robert Lu
| m_older_l : Older Mary Lu

/-
Natural Language Proof:
    if a relationship is transitive it means that
    two existences of the relationship can be combined
    to show a proof of the new relationship.
    if a > b and b>c, a>c

-/

/- B. Formal Proofs -/

/-
Given formal proofs of the following
propositions in predicate logic.
-/

/-
Note: The keyword, "example", in the following
problems is just like theorem but just omits
giving give a name to a proof.
-/

example : ∀ (P Q : Prop), P → Q → (Q ∧ P) :=
    begin
        intros,
        exact and.intro a_1 a
    end

example : ∀ (P Q : Prop), P ∧ Q → P :=
    begin
    intros,
    exact and.elim_left a,
end

example : ∀ (P Q R : Prop), (P ∧ Q) → (Q ∧ R) → (P ∧ R) :=
    begin
        intros P Q R pandq qandr,
        have p := and.elim_left pandq,
        have r := and.elim_right qandr,
        exact and.intro p r,
    end

example : ∀ (P Q : Prop), ((P → Q) ∧ P) → Q :=
    begin
        intros P Q x,
        have pimpq := and.elim_left x,
        apply pimpq,
        apply and.elim_right x,
    end

example : ∀ (P Q : Prop), (P ∧ Q) → (Q ∧ P) :=
    begin
        intros P Q pandq,
        apply and.intro (and.elim_right pandq)(and.elim_left pandq)
    end

-- Extra credit questions

example : ∀ (a b c : ℕ), a = b → b = c → a = c :=
    begin
        intros a b c aeqb beqc,
        apply eq.subst,
        apply beqc,
        apply aeqb,
    end

example : ∀ (P Q : Prop), (P → Q) → (¬ Q → ¬ P) :=
    begin
        intros P Q pimpq nq,
    end


-- Math 52: Week 4

import .utils.int_refl

-- Lakins Definition 1.2.1:
definition is_even (n : ℤ) := ∃ (k : ℤ), n = 2 * k
definition is_odd (n : ℤ) := ∃ (k : ℤ), n = 2 * k + 1

-- Lakins Definition 2.1.1: Let a, b ∈ ℤ.
-- a divides b if there exists n ∈ ℤ such that b = an.
-- We write a ∣ b for "a divides b" and say that a is
-- a divisor of b.
definition divides (a b : ℤ) : Prop := ∃ (n : ℤ), a * n = b

-- The notation `a ∣ b` can be used for `divides a b`
local infix ∣ := divides

-- Lakins Example 2.1.2:
example : 3 ∣ 12 :=
begin
unfold divides,
existsi (4:ℤ),
refl,
end

-- Theorem: For every integer a, a ∣ a.
theorem divides_refl : ∀ (a : ℤ), a ∣ a :=
begin
intro a,
unfold divides,
existsi (1:ℤ),
calc a * 1 = a : by rw mul_one,
end
-- Proof: Let a ∈ ℤ be arbitrary. We must show that a ∣ a;
-- i.e., we must find an integer k such that a = a * k.
-- Since a = a * 1 and 1 is an integer, we see that
-- a ∣ a is true. □

-- Lakins Proposition 2.1.3: For all integers a, b, c,
-- if a ∣ b and b ∣ c, then a ∣ c.
theorem divides_trans : ∀ (a b c : ℤ), a ∣ b ∧ b ∣ c → a ∣ c :=
begin
intros a b c,
intro H,
cases H,
unfold divides,
unfold divides at H_left H_right,
cases H_left with n,
cases H_right with m,
existsi(n*m:ℤ),
symmetry,
calc c
=b*m : by rw H_right_h ...
=(a*n)*m : by rw H_left_h ...
=a*(n*m): by ac_refl,
end
-- Proof: Let a,b,c ∈ ℤ be arbitrary and assume that
-- a ∣ b and b ∣ c. We must show that a ∣ c; i.e., we
-- must find an integer k such that c = a * k.
--
-- Since a ∣ b, by Definition 2.1.1 we may fix n ∈ ℤ
-- such that b = a * n. Similarly, since b | c, we may
-- fix m ∈ ℤ such that c = b * m, again by Definition
-- 2.1.1. Then
--   c = b * m = (a * n) * m = a * (n * m),
-- since multiplication of integers is associative
-- (Basic Properties of Integers 1.2.3). Since
-- n * m ∈ ℤ, we have proved that a ∣ c, by
-- Definition 2.1.1, as desired. 􏰟

-- Lakins Exercise 2.1.1: Let a,b, and c be integers.
-- For all integers m and n, if a ∣ b and a ∣ c, then
-- a ∣ (bm + cn).
theorem L211 : ∀ (a b c m n : ℤ), a ∣ b ∧ a ∣ c → a ∣ (b * m + c * n) :=
begin
sorry
end

-- Theorem: For every integer a, a is even if and only if 2 ∣ a.
theorem is_even_iff_two_divides : ∀ (a : ℤ), is_even a ↔ 2 ∣ a :=
begin
sorry
end

-- We will prove this fact later after we discuss induction.
-- For now we take it as an axiom, i.e., as statement that we
-- take as true without proof.
axiom even_or_odd (a : ℤ) : is_even a ∨ is_odd a

-- Lakins Theorem 2.1.9: For all integers a, a(a + 1) is even.
theorem even_product : ∀ (a : ℤ), is_even (a * (a + 1)) :=
begin
sorry
end
-- Proof: Let a ∈ ℤ. We show that a(a + 1) is even by
-- considering two cases.
--
-- Case I: a is even.
-- Then 2 ∣ a, by Definition 1.2.1. Since a ∣ a(a + 1)
-- by Definition 2.1.1, we have that 2 ∣ a(a + 1) since
-- the divisibility relation is transitive (Proposition
-- 2.1.3). Hence a(a + 1) is even.
--
-- Case II: a is not even.
-- Since a is not even, we know that a is odd. Then
-- a+1 is even by Exercise 1.2.2b. Then, using an
-- argument similar to that of Case I, we have that
-- 2 ∣ (a+1) and (a+1) ∣ a(a+1), and hence 2 ∣ a(a+1)
-- by Proposition 2.1.3. Thus a(a + 1) is even.
--
-- Hence, since we have considered all possible cases
-- for the integer a, we have proved that for all
-- integers a, a(a + 1) is even. 􏰟


/- © 2021 Derik Kauffman. Licensed under the MIT license. -/

import tactic.induction
import tactic.linarith

/-! # Dual numbers
The [__dual numbers__](https://en.wikipedia.org/wiki/Dual_number)
are a number system consisting of pairs of numbers, `a + bε`, where `ε² = 0`.
I'll refer to `a` as the _real part_ and `b` as the _infinitesimal part_.
I'm using rational numbers for the coefficients `a` and `b`, since reals are
uncomputable in Lean.
-/
structure dual := (a b : ℚ)

namespace dual
instance : has_repr dual :=
	{repr := (λd : dual, to_string d.a ++ " + " ++ to_string d.b ++ "ε")}

def of_rat (q : ℚ) : dual := mk q 0

@[simp] lemma eq_components_eq (p q : dual) :
	p = q ↔ p.a = q.a ∧ p.b = q.b :=
begin
	induction' p,
	induction' q,
	simp,
end

-- We define basic operations on the duals, and register them to take advantage
-- of standard symbolic notation:
@[simp] def add (p q : dual) := mk (p.a + q.a) (p.b + q.b)
instance : has_add dual := ⟨add⟩

@[simp] lemma add_eq (p q : dual) :
  p + q = { a := p.a + q.a, b := p.b + q.b } := by refl

@[simp] def neg (p : dual) := mk (-p.a) (-p.b)
instance : has_neg dual := ⟨neg⟩

@[simp] lemma neg_eq (p : dual) :
  -p = { a := -p.a, b := -p.b } := by refl

-- The multiplication rule captures the fact that ε² = 0:
@[simp] def mul (p q : dual) := mk (p.a * q.a) (p.a * q.b + p.b * q.a)
instance : has_mul dual := ⟨mul⟩

@[simp] lemma mul_eq (p q : dual) :
  p * q = { a := p.a * q.a, b := p.a * q.b + p.b * q.a } := by refl

@[simp] def zero : dual := mk 0 0
instance : has_zero dual := ⟨zero⟩

@[simp] def one : dual := mk 1 0
instance : has_one dual := ⟨one⟩

-- Here we prove that the dual numbers form a commutative ring:
instance dual : comm_ring dual :={
	add       := add,
  add_assoc := by simp [add_assoc],
	zero      := zero,
	zero_add  := by intro a; induction' a; simp,
	add_zero  := by intro a; induction' a; simp,
	neg       := neg,
	mul       := mul,
	add_left_neg  := by finish,
	add_comm      := by intros; simp [add_comm],
	mul_assoc     := by intros; simp [mul_assoc]; ring,
	one           := one,
	one_mul       := by intro a; induction' a; simp,
	mul_one       := by intro a; induction' a; simp,
	left_distrib  := by intros; simp [left_distrib]; ring,
	right_distrib := by intros; simp [right_distrib]; ring,
	mul_comm      := by intros; simp [mul_comm]; ring
}

-- For convenience, we define `ε` as the unit infinitesimal
def ε : dual := mk 0 1

-- This allows us to write dual numbers quite naturally:
#eval 3 + 4*ε  -- 3 + 4ε
#eval (1 + ε) * ε  -- 0 + 1ε

-- We can define a division on the ring as the inverse of multiplication:
def div (x y : dual) := mk (x.a / y.a) ((x.b * y.a - x.a * y.b)/(y.a^2))
instance : has_div dual := ⟨div⟩
-- Look familiar? This is just the quotient rule.

/- Technically 1/ε should be undefined, but our division rule gives it a value
of 0 (just like how we define 1/0 ≡ 0 for rationals) -/
#eval 1/ε  -- 0 + 0ε

-- However, there is no true inverse to ε (no dual number x such that x * ε = 1):
lemma no_inv_of_ε : ∀x : dual, x * ε ≠ 1 :=
begin
	intro x,
	induction' x,
	rw ε,

	calc (mk a b) * (mk 0 1) = mk 0 a : by simp
	... ≠ one : by finish,
end

/- We can use the dual numbers to implement a basic automatic differentiation
system! Given any function defined in terms of addition, subtraction,
multiplication, and division, we can find its derivative at a point x
by plugging in x + ε and taking the infinitesimal part of the result:
-/
def poly := λx : dual, x^8 + 4*x^5 - 2*x^3 + x - 3
def ratfunc := λx : dual, (x^3 - 2*x^2 - 4*x + 5) / (2*x + 3)

def derivative (f : dual → dual) (x : ℚ) := (f (mk x 1)).b
#eval derivative poly (-2)  -- -727
#eval derivative ratfunc 3  -- 95/81

end dual


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

import tactic -- imports all the Lean tactics
import data.polynomial -- API for (i.e. definitions relating to and lemmas about) polynomials

/-!

# Degree

What is the degree of the zero polynomial? Is it 0, -1 or -∞? One advantage
of -∞ is that things like `deg(f*g)=deg(f)*deg(g)` is always true (at least
if the base is an integral domain) but one big disadvantage is that working
with a type like `with_bot ℕ` (which is the naturals plus a "bottom" element
`⊥` which is less than all naturals) is a pain; it's sometimes much easier to
define `deg(0)` to be the "junk" value `0` and have a degree function
taking values in the naturals.

Lean has both options: there is a function `polynomial.degree` taking
values in `with_bot ℕ` and also a function `polynomial.nat_degree`
taking values in `ℕ`. In this file we use `nat_degree` because for
what we're doing here the benefits outweigh the gains.

## M(R,n), the space of polynomials of degree ≤ n

We are about to embark upon a long proof; a proof that if `R` is a Noetherian
ring (i.e. all its ideals are finitely-generated) then so is `R[X]`.
One of the players in it will be the free `R`-module of polynomials
of degree at most `n`. We make this definition explicitly. I've
left the proofs of the axioms for you to fill in; you are going to have
to figure out the API for `polynomial.nat_degree` to solve them. Guess
the names of the lemmas you'll need; everything is there somewhere.
If you really can't guess, then browse around the API documentation
for `nat_degree` here:

https://leanprover-community.github.io/mathlib_docs/data/polynomial/degree/definitions.html#polynomial.nat_degree

-/

namespace polynomial

variables (R : Type) [comm_ring R]

#check nat_degree_smul_le

/-- `M R n` is the `R`-submodule of `R[X]` consisting of polynomials of degree ≤ `n`. -/
noncomputable def M (n : ℕ) : submodule R (polynomial R) :=
{ carrier := {f : polynomial R | f.nat_degree ≤ n},
  zero_mem' := zero_le n,
  add_mem' := λ p q hpn hqn, le_trans (nat_degree_add_le p q) (max_le hpn hqn),
  smul_mem' := λ c p hpn, le_trans (nat_degree_smul_le c p) hpn,
}

-- think about what `f ∈ M R n` means

lemma mem_M_iff (f : polynomial R) (n : ℕ) : f ∈ M R n ↔ f.nat_degree ≤ n :=
begin
  refl,
end

/-

## lcoeff

The function `polynomial.coeff` eats a polynomial `f` and a natural `n`, and
returns the coefficient of `X^n` in `f`. However this is just a bare function.
For fixed `n`, the function which eats `f` and returns the coefficient of
`X^n` in `f` is an `R`-linear map, and this `R`-linear map
is called `polynomial.lcoeff R n : polynomial R →ₗ[R] R`. The notation
`→ₗ[R]` means "R-linear map".

The lemma below, which will be of use for us later, says that a polynomial
of degree ≤ n+1 whose coefficient of X^{n+1} is 0, is actually a polynomial
of degree ≤ n. The proof is not too bad once you know about
`nat_degree_le_iff_coeff_eq_zero`.

-/

lemma ker_lcoeff (f : polynomial R) (n : ℕ) (hfn : f ∈ M R (n+1)) (hf : lcoeff R (n+1) f = 0) :
  f ∈ M R n :=
begin
  rw [mem_M_iff, nat_degree_le_iff_coeff_eq_zero] at hfn ⊢,
  intros i hni,
  rw [nat.lt_iff_add_one_le, le_iff_lt_or_eq] at hni,
  rcases hni with (hni | rfl),
  { exact hfn i hni },
  { exact hf },
end

/-

Finally, this lemma below, saying that the degree of X^n is at most n,
seems to be missing from mathlib. You would have thought that this
was a strange thing to prove because surely the degree of X^n *equals* n,
but in fact there is an edge case where this is false! Can you think of it?

Spoiler alert below.

If R is the zero ring, then R[X] is the zero ring, and X^n=X^2=X=1=0
so clearly X^n can't have degree n. It turns out that
there's a lemma `nat_degree_of_subsingleton` saying that if `R`
is a so-called "subsingleton" (mathlib's preferred say of saying
that `R` is the zero ring) then every polynomial has `nat_degree` 0.
For `nontrivial` rings (those with `0 ≠ 1`), `nat_degree_X_pow`
works. So your proof could do `cases` on `subsingleton_or_nontrivial R`,
except that because `subsingleton` and `nontrivial` are both typeclasses,
you should use the `casesI` tactic to ensure that the assumptions
are known to the type class inference system.

-/

#check polynomial.nat_degree_X_pow

-- this should be PR'ed to mathlib
lemma nat_degree_X_pow_le (n : ℕ) : (X^n : polynomial R).nat_degree ≤ n :=
begin
  casesI subsingleton_or_nontrivial R,
  { rw nat_degree_of_subsingleton, exact zero_le n },
  { exact le_of_eq (polynomial.nat_degree_X_pow _) },
end

end polynomial


theorem ex1 : (fun y => y + 0) = (fun x => 0 + x) := by
  funext x
  simp

theorem ex2 : (fun y x => y + x + 0) = (fun x y => y + x) := by
  funext x y
  rw [Nat.add_zero, Nat.add_comm]

theorem ex3 : (fun (x : Nat × Nat) => x.1 + x.2) = (fun (x : Nat × Nat) => x.2 + x.1) := by
  funext (a, b)
  show a + b = b + a
  rw [Nat.add_comm]

theorem ex4 : (fun (x : Nat × Nat) (y : Nat × Nat) => x.1 + y.2) = (fun (x : Nat × Nat) (z : Nat × Nat) => z.2 + x.1) := by
  funext (a, b) (c, d)
  show a + d = d + a
  rw [Nat.add_comm]

theorem ex5 : (fun (x : Id Nat) => x.succ + 0) = (fun (x : Id Nat) => 0 + x.succ) := by
  funext (x : Nat)
  have y := x + 1 -- if `(x : Nat)` is not used at `funext`, then `x+1` would fail to be elaborated since we don't have the instance `Add (Id Nat)`
  rw [Nat.add_comm]

theorem ex6 : (fun (x : Nat) y z => x + y + z) = (fun x y z => x + (y + z)) := by
  funext
  rw [Nat.add_assoc]


import tutorial_world.level17_by_cases --hide
open IncidencePlane --hide

/- Tactic : by_contra

## Summary

It changes the goal from `⊢ P` to `⊢ false` and adds a new hypothesis
of the form `h : ¬ P` to the local context.

## Real Life Example

If we want to prove `⊢ SKY_ALWAYS_BLUE`, but we assume that `h : SKY_GREY_WHEN_RAINS`,
type `by_contra z` to add `z : ¬ (SKY_ALWAYS_BLUE)` to the local context and change the
goal into `⊢ false`. Then, argue that `h : SKY_GREY_WHEN_RAINS` to finish the proof.
-/

/-
# Tutorial World

## Level 18: the `by_contra` tactic.

Congratulations! You are about to finish the first world of The Euclid Game!
In this level, we introduce the `by_contra` tactic. Mathematicians would use
it to provide a **proof by contradiction**. This tactic changes the goal from
`⊢ P` to `⊢ false` and adds a new hypothesis of the form `h : ¬ P` to the local context.

To finish this world, we would like to prove that two distinct lines have **at most**
one point in common. Delete the `sorry` to see the goal appear as `⊢ A = B`. Now, take a look
to the hypotheses that we have in our local context and try to do a drawing of the situation
by using all of them. Once you're done, note that the points A and B must be equal so that the
lines r and s satisfy the hypothesis `hrs: r ≠ s`. Then, try to look for a theorem statement which
could be useful for this level. As you've well deduced, `equal_lines_of_contain_two_points`
is the right path to choose. However, note that it states `A ≠ B` and `r = s` instead of `A = B`and
`r ≠ s`, respectively. Because of this reason, the `by_contra` tactic has to join the party.

Now, try to solve this level by your own in just three lines of code. [**Remember:** whenever you see
a hypothesis of the form `h : P ≠ Q`, Lean can also understand it as `h : ¬ (P = Q)`, or `h : (P = Q) → false`.]
-/

/- Hint : Click here for a hint, in case you get stuck.
Starting the proof by typing `by_contra h,` will change the goal into proving `⊢ false` and add the hypothesis `h : ¬ (A = B)` to the
local context. Now, note that `hrs : r ≠ s` can be also understood as `h : (r = s) → false`. Then, `apply hrs,` will make progress. To finish with,
try to close the goal in just one line by using the `equal_lines_of_contain_two_points` theorem statement.
Still bewildered? Click on "View source" (located on the top right corner of the game screen) to see the solution.
-/

variables {Ω : Type} [IncidencePlane Ω] --hide
variables {A B : Ω} --hide
variables {r s : Line Ω} --hide
/- Lemma :
Two distinct lines have at most one point in common.
-/
lemma distinct_lines_have_at_most_one_common_point
	(hrs: r ≠ s)
	(hAr: A ∈ r) (hAs: A ∈ s) (hBr: B ∈ r) (hBs: B ∈ s) :
	A = B :=
begin

  by_contra h,
  apply hrs,
  apply equal_lines_of_contain_two_points h hAr hAs hBr hBs,

end


import data.real.basic
import data.int.parity

/-
In this file, we learn how to handle the ∃ quantifier.

In order to prove `∃ x, P x`, we give some x₀ using tactic `use x₀` and
then prove `P x₀`. This x₀ can be an object from the local context
or a more complicated expression.
-/
example : ∃ n : ℕ, 8 = 2*n :=
begin
  use 4,
  refl,  -- this is the tactic analogue of the rfl proof term
end

/-
In order to use `h : ∃ x, P x`, we use the `cases` tactic to fix
one x₀ that works.

Again h can come straight from the local context or can be a more
complicated expression.
-/
example (n : ℕ) (h : ∃ k : ℕ, n = k + 1) : n > 0 :=
begin
  -- Let's fix k₀ such that n = k₀ + 1.
  cases h with k₀ hk₀,
  -- It now suffices to prove k₀ + 1 > 0.
  rw hk₀,
  -- and we have a lemma about this
  exact nat.succ_pos k₀,
end

/-
The next exercises use divisibility in ℤ (beware the ∣ symbol which is
not ASCII).

By definition, a ∣ b ↔ ∃ k, b = a*k, so you can prove a ∣ b using the
`use` tactic.
-/

-- Until the end of this file, a, b and c will denote integers, unless
-- explicitly stated otherwise
variables (a b c : ℤ)

-- 0029
example (h₁ : a ∣ b) (h₂ : b ∣ c) : a ∣ c :=
begin
  cases h₁ with x hx,
  cases h₂ with y hy,
  rw hx at hy,
  use (x*y),
  linarith,
end

/-
A very common pattern is to have an assumption or lemma asserting
  h : ∃ x, y = ...
and this is used through the combo:
  cases h with x hx,
  rw hx at ...
The tactic `rcases` allows us to do recursive `cases`, as indicated by its name,
and also simplifies the above combo when the name hx is replaced by the special
name `rfl`, as in the following example.
It uses the anonymous constructor angle brackets syntax.
-/

example (h1 : a ∣ b) (h2 : a ∣ c) : a ∣ b+c :=
begin
  rcases h1 with ⟨k, rfl⟩,
  rcases h2 with ⟨l, rfl⟩,
  use k+l,
  ring,
end

/-
You can use the same `rfl` trick with the `rintros` tactic.
-/

example : a ∣ b → a ∣ c → a ∣ b+c :=
begin
  rintros ⟨k, rfl⟩ ⟨l, rfl⟩,
  use k+l,
  ring,
end

-- 0030
example : 0 ∣ a ↔ a = 0 :=
begin
  split,
  rintros ⟨k, rfl⟩,
  ring,
  intro h,
  rw h,
end

/-
We can now start combining quantifiers, using the definition

  surjective (f : X → Y) := ∀ y, ∃ x, f x = y

-/
open function

-- In the remaining of this file, f and g will denote functions from
-- ℝ to ℝ.
variables (f g : ℝ → ℝ)

-- 0031
example (h : surjective (g ∘ f)) : surjective g :=
begin
  intros a,
  rcases h a with ⟨k, rfl⟩,
  use f(k),
end

/-
The above exercise can be done in three lines. Try to do the
next exercise in four lines.
-/

-- 0032
example (hf : surjective f) (hg : surjective g) : surjective (g ∘ f) :=
begin
  intros a,
  rcases hg a with ⟨k, rfl⟩,
  rcases hf k with ⟨j, rfl⟩,
  use j,
end


import data.real.basic

open_locale classical

/-
Theoretical negations.

This file is for people interested in logic who want to fully understand
negations.

Here we don't use `contrapose` or `push_neg`. The goal is to prove lemmas
that are used by those tactics. Of course we can use
`exfalso`, `by_contradiction` and `by_cases`.

If this doesn't sound like fun then skip ahead to the next file.
-/

section negation_prop

variables P Q : Prop

-- 0055
example : (P → Q) ↔ (¬ Q → ¬ P) :=
begin
  split,
    intros h hnq hnp,
    exact hnq (h hnp),
  intros h p,
  by_contradiction hnq,
  exact h hnq p,
end

-- 0056
lemma non_imp (P Q : Prop) : ¬ (P → Q) ↔ P ∧ ¬ Q :=
begin
  split,
    intros h,
    by_contradiction c,
    apply h,
    intro hp,
    by_contradiction c',
    apply c,
    exact ⟨hp, c'⟩,
  rintro ⟨hp, hnq⟩ h,
  exact hnq (h hp),
end

-- In the next one, let's use the axiom
-- propext {P Q : Prop} : (P ↔ Q) → P = Q

-- 0057
example (P : Prop) : ¬ P ↔ P = false :=
begin
  split,
    intro h,
    apply propext,
    split,
      intro p,
      exact h p,
    intro c,
    exfalso,
    exact c,
  intro h,
  rw h,
  exact id,
end

end negation_prop

section negation_quantifiers
variables (X : Type) (P : X → Prop)

-- 0058
example : ¬ (∀ x, P x) ↔ ∃ x, ¬ P x :=
begin
  split,
    intro h,
    by_contradiction c,
    apply h,
    intro x,
    by_contradiction c',
    apply c,
    use x,
  rintros ⟨x, hx⟩ h,
  exact hx (h x),
end

-- 0059
example : ¬ (∃ x, P x) ↔ ∀ x, ¬ P x :=
begin
  split,
    intros h x,
    by_contradiction c,
    apply h,
    use [x, c],
  rintro h ⟨x, hc⟩,
  exact h x hc,
end

-- 0060
example (P : ℝ → Prop) : ¬ (∃ ε > 0, P ε) ↔ ∀ ε > 0, ¬ P ε :=
begin
  split,
    intros h ε ε_pos c,
    apply h,
    use [ε, ε_pos, c],
  rintro h ⟨ε, ε_pos, c⟩,
  exact h ε ε_pos c,
end

-- 0061
example (P : ℝ → Prop) : ¬ (∀ x > 0, P x) ↔ ∃ x > 0, ¬ P x :=
begin
  split,
    intro h,
    by_contradiction c,
    apply h,
    intros x hx,
    by_contradiction c',
    apply c,
    use [x, hx, c'],
  rintro ⟨x, hx, hx'⟩ c,
  exact hx' (c x hx),
end

end negation_quantifiers


-- Asociatividad_del_minimo.lean
-- Si a, b, c ∈ ℝ, entonces min(min(a,b),c) = min(a,min(b,c))
-- José A. Alonso Jiménez <https://jaalonso.github.io>
-- Sevilla, 3-octubre-2022
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Sean a, b y c números reales. Demostrar que
--    min (min a b) c = min a (min b c)
-- ----------------------------------------------------------------------

import data.real.basic tactic

variables a b c : ℝ

-- 1ª demostración
-- ===============

-- Se usará el siguiente lema auxilar.
lemma aux1 : min (min a b) c ≤ min a (min b c) :=
begin
  apply le_min,
  { show min (min a b) c ≤ a,
      calc min (min a b) c
           ≤ min a b : min_le_left (min a b) c
       ... ≤ a       : min_le_left a b },
  { show min (min a b) c ≤ min b c,
    apply le_min,
    { show min (min a b) c ≤ b,
        calc min (min a b) c
             ≤ min a b  : min_le_left (min a b) c
         ... ≤ b        : min_le_right a b },
    { show min (min a b) c ≤ c,
      { by exact min_le_right (min a b) c }}},
end

example :
  min (min a b) c = min a (min b c) :=
begin
  apply le_antisymm,
  { show min (min a b) c ≤ min a (min b c),
      by exact aux1 a b c },
  { show min a (min b c) ≤ min (min a b) c,
      calc min a (min b c)
           = min (min b c) a : by apply min_comm
       ... = min (min c b) a : by {congr' 1, apply min_comm}
       ... ≤ min c (min b a) : by apply aux1
       ... = min c (min a b) : by {congr' 1, apply min_comm}
       ... = min (min a b) c : by apply min_comm },
end

-- 2ª demostración
-- ===============

-- Se usará el siguiente lema auxilar.
lemma aux2 : min (min a b) c ≤ min a (min b c) :=
begin
  apply le_min,
  { calc min (min a b) c
         ≤ min a b : by simp [min_le_left]
     ... ≤ a       : by simp [min_le_left] },
  { apply le_min,
    { calc min (min a b) c
           ≤ min a b  : by simp [min_le_left]
       ... ≤ b        : by simp [min_le_right] },
    { simp [min_le_right] }},
end

example : min (min a b) c = min a (min b c) :=
begin
  apply le_antisymm,
  { by exact aux2 a b c },
  { calc min a (min b c)
         = min (min b c) a : by simp [min_comm]
     ... = min (min c b) a : by simp [min_comm]
     ... ≤ min c (min b a) : by simp [aux2]
     ... = min c (min a b) : by simp [min_comm]
     ... = min (min a b) c : by simp [min_comm] },
end

-- 3ª demostración
-- ===============

example : min (min a b) c = min a (min b c) :=
begin
  apply le_antisymm,
  { by exact aux2 a b c },
  { calc min a (min b c)
         = min (min c b) a : by simp [min_comm]
     ... ≤ min c (min b a) : by simp [aux2]
     ... = min (min a b) c : by simp [min_comm] },
end


-- Exercise 1
--
-- Prove these equivalences:
section
  variables (α : Type) (p q : α → Prop)

  example : (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x) :=
  begin
    split,

    {
      assume h,
      split,

      {
        assume hx,
        exact and.left (h hx),
      },

      {
        assume hx,
        exact and.right (h hx),
      },
    },

    {
      assume h,
      assume hx,

      exact and.intro (and.left h hx) (and.right h hx),
    },
  end

  example : (∀ x, p x → q x) → (∀ x, p x) → (∀ x, q x) :=
  begin
    assume hpq,
    assume hp,
    assume x,

    exact hpq x (hp x),
  end

  example : (∀ x, p x) ∨ (∀ x, q x) → ∀ x, p x ∨ q x :=
  begin
    have f : (∀ x, p x) → ∀ x, p x ∨ q x,
    {
      assume hpx,
      assume hx,

      exact or.intro_left (q hx) (hpx hx),
    },

    have g : (∀ x, q x) → ∀ x, p x ∨ q x,
    {
      assume hqx,
      assume hx,

      exact or.intro_right (p hx) (hqx hx),
    },

    assume h,
    exact or.elim h f g,

    -- NOTE: Not derivable because there exist situations in which
    --       (∀ x, p x ∨ q x) is true, but (∀ x, p x) ∨ (∀ x, q x) is not. The
    --       former can mix truthiness between the two, but the latter requires
    --       that p must hold for all x, or q must hold for all x.
  end
end

-- Exercise 2
--
-- It is often possible to bring a component of a formula outside a universal
-- quantifier, when it does not depend on the quantified variable. Try proving
-- these (one direction of the second of these requires classical logic):
section
  universe u
  variables (α : Type u) (p q : α → Prop)
  variable r : Prop

  example : α → ((∀ x : α, r) ↔ r) :=
  begin
    assume ha,
    split,

    assume har,
    exact har ha,

    assume hr,
    assume haa,
    exact hr,
  end

  variable x : α

  example : (∀ x, p x ∨ r) ↔ (∀ x, p x) ∨ r :=
  begin
    split,

    -- hphx : p hx = evidence of the predicate p holding on an element of x
    -- p hx : Prop = a property that may or may not be true of hx

    {
      assume h,

      have pos : r → (∀ x, p x) ∨ r,
      {
        assume hr,
        exact or.inr hr,
      },

      have neg : ¬r → (∀ x, p x) ∨ r,
      {
        assume hnr,

        apply or.inl,
        assume hx,

        exact or.elim (h hx) id (λ hr, absurd hr hnr),
      },

      exact classical.by_cases pos neg,
    },

    {
      assume h,
      assume hx,

      exact
        or.elim
        h
        (λ hl, or.intro_left r (hl hx))
        (λ hr, or.intro_right (p hx) hr),
    },
  end

  example : (∀ x, r → p x) ↔ (r → ∀ x, p x) :=
  begin
    split,

    {
      assume h,
      assume hr,
      assume ha,
      exact (h ha) hr,
    },

    {
      assume h,
      assume ha,
      assume hr,
      exact (h hr) ha,
    },
  end
end

-- Exercise 3
--
-- Consider the “barber paradox,” that is, the claim that in a certain town
-- there is a (male) barber that shaves all and only the men who do not shave
-- themselves. Prove that this is a contradiction:
section
  variables (men : Type) (barber : men)
  variable  (shaves : men → men → Prop)
  variable (h : ∀ x : men, shaves barber x ↔ ¬ shaves x x)

  example (h : ∀ x : men, shaves barber x ↔ ¬ shaves x x) : false :=
  begin
    have hiff : (shaves barber barber) ↔ ¬(shaves barber barber), from h barber,

    have f : shaves barber barber → false,
    {
      assume hf,
      exact (hiff.mp hf) hf,
    },

    have g : ¬shaves barber barber → false,
    {
      assume hg,
      exact f (hiff.mpr hg),
    },

    exact classical.by_cases f g,
  end
end

-- Exercise 4
--
-- Below, we have put definitions of divides and even in a special namespace to
-- avoid conflicts with definitions in the library. The instance declaration
-- makes it possible to use the notation m | n for divides m n. Don’t worry
-- about how it works; you will learn about that later.
namespace hidden
  def divides (m n : ℕ) : Prop := ∃ k, m * k = n

  instance : has_dvd nat := ⟨divides⟩

  def even (n : ℕ) : Prop := 2 ∣ n -- You can enter the '∣' character by typing \mid

  section
  variables m n : ℕ

  #check m ∣ n
  #check m^n
  #check even (m^n +3)
  end
end hidden

-- Remember that, without any parameters, an expression of type Prop is just an
-- assertion. Fill in the definitions of prime and Fermat_prime below, and
-- construct the given assertion. For example, you can say that there are
-- infinitely many primes by asserting that for every natural number n, there is
-- a prime number greater than n. Goldbach’s weak conjecture states that every
-- odd number greater than 5 is the sum of three primes. Look up the definition
-- of a Fermat prime or any of the other statements, if necessary.
section
  def prime (n : ℕ) : Prop :=
    ¬∃m: ℕ, m < n ∧ hidden.divides m n

  def infinitely_many_primes : Prop :=
    ∀x : ℕ, ∃y : ℕ, y > x ∧ prime y

  def Fermat_prime (n : ℕ) : Prop :=
    ∃k : ℕ, n = 2^(2 ^ k) + 1

  def infinitely_many_Fermat_primes : Prop :=
    ∀x : ℕ, ∃y : ℕ, y > x ∧ Fermat_prime y

  def goldbach_conjecture : Prop :=
    ∀x : ℕ, x > 2 → ∃y z : ℕ, x = y + z ∧ prime y ∧ prime z

  def Goldbach's_weak_conjecture : Prop :=
    ∀x : ℕ, ¬hidden.even x ∧ x > 5 → ∃a b c : ℕ, x = a + b + c ∧ prime a ∧ prime b ∧ prime c

  def Fermat's_last_theorem : Prop :=
    ¬∃a b c n : ℕ, n > 2 ∧ a^n = b^n + c^n
end

-- Exercise 5
--
-- Prove as many of the identities listed in Section 4.4 as you can.
section
  open classical

  variables (α : Type) (p q : α → Prop)
  variable a : α
  variable r : Prop

  include a

  example : (∃ x : α, r) → r :=
  begin
    assume h,

    have hq : (∀a : α, r → r),
    assume a,
    assume hr,
    exact hr,

    exact exists.elim h hq,
  end

  example : r → (∃ x : α, r) :=
  begin
    assume hr,

    exact exists.intro a hr
  end

  example : (∃ x, p x ∧ r) ↔ (∃ x, p x) ∧ r :=
  begin
    split,

    {
      assume h,

      have hq : ∀(a : α), p a ∧ r → (∃(x : α), p x) ∧ r,
      {
        assume ha,
        assume conj,

        apply and.intro,
        exact exists.intro ha (and.left conj),
        exact and.right conj,
      },

      exact exists.elim h hq,
    },

    {
      assume h,
      have hl, from and.left h,
      have hr, from and.right h,

      have hq : ∀(a : α), p a → (∃x : α, p x ∧ r),
      {
        assume ha,
        assume h',
        apply exists.intro,

        exact and.intro h' hr,
      },

      exact exists.elim hl hq,
    },
  end

  example (h : ∃ x, p x ∧ q x) : ∃ x, q x ∧ p x :=
  match h with ⟨w, hw⟩ :=
    ⟨w, hw.right, hw.left⟩
  end

  example : (∃ x, p x ∨ q x) ↔ (∃ x, p x) ∨ (∃ x, q x) :=
  begin
    split,

    {
      assume h,

      have f : Π a : α, p a → ∃ (x : α), p x,
      exact exists.intro,

      have g : Π a : α, q a → ∃ (x : α), q x,
      exact exists.intro,

      apply exists.elim h,
      assume ha,
      assume hor,

      exact
        or.elim
        hor
        (λ hpa, or.inl (f ha hpa))
        (λ hqa, or.inr (g ha hqa)),
    },

    {
      assume hor,

      have f : (∃ (x : α), p x) → ∃ (x : α), p x ∨ q x,
      assume hex,
      exact (
        match hex with ⟨ha, hpx⟩ :=
          ⟨ha, or.inl hpx⟩
        end
      ),

      have g : (∃ (x : α), q x) → ∃ (x : α), p x ∨ q x,
      assume hex,
      exact (
        match hex with ⟨ha, hqx⟩ :=
          ⟨ha, or.inr hqx⟩
        end
      ),

      exact or.elim hor f g,
    },
  end

  example : (∀ x, p x) ↔ ¬ (∃ x, ¬ p x) :=
  begin
    split,

    {
      assume hfa,
      assume hex,

      have f : (∀ (a : α), (λ (x : α), ¬p x) a → false),
      assume a,
      assume hnpa,
      exact hnpa (hfa a),

      exact exists.elim hex f,
    },

    {
      assume hne,
      assume a,

      apply by_contradiction,
      assume hnpa,

      have he : ∃x : α, ¬p x,
      exact exists.intro a hnpa,

      exact hne he,
    },
  end

  example : (∃ x, p x) ↔ ¬ (∀ x, ¬ p x) :=
  begin
    split,

    {
      assume hex,
      assume hfa,

      have f : (∀ (a : α), (λ (x : α), p x) a → false),
      assume ha,
      assume hpa,
      exact (hfa ha) hpa,

      exact exists.elim hex f,
    },

    {
      assume hnfa,
      apply by_contradiction,
      assume hnex,

      have hfa : ∀(x : α), ¬p x,
      assume ha,
      assume hpa,
      exact hnex (exists.intro ha hpa),

      exact hnfa hfa,
    },
  end

  example : (¬ ∃ x, p x) ↔ (∀ x, ¬ p x) :=
  begin
    split,

    {
      assume hnex,
      assume ha,
      assume hpa,

      have hex : ∃(x : α), p x,
      exact exists.intro ha hpa,

      exact hnex hex,
    },

    {
      assume hfa,
      assume hex,

      have f : (∀ x : α, (λ a : α, p x) a → false),
      assume ha,
      assume hpa,
      exact (hfa ha) hpa,

      exact exists.elim hex f,
    },
  end

  example : (¬ ∀ x, p x) ↔ (∃ x, ¬ p x) :=
  begin
    split,

    {
      assume hnfa,
      apply by_contradiction,
      assume hnex,

      have hfa : ∀ (x : α), p x,
      assume ha,
      apply by_contradiction,
      assume hpnha,
      exact hnex ⟨ha, hpnha⟩,

      exact hnfa hfa,
    },

    {
      assume hex,
      assume hfa,

      exact (
        match hex with ⟨ha, hnpa⟩ :=
          hnpa (hfa ha)
        end
      ),
    },
  end

  example : (∀ x, p x → r) ↔ (∃ x, p x) → r :=
  begin
    split,

    {
      assume hfa,
      assume hex,

      have f : (∀ (x : α), (λ (y : α), p y) x → r),
      assume ha,
      assume hpa,
      exact (hfa ha) hpa,

      exact exists.elim hex f,
    },

    {
      assume h,
      assume ha,
      assume hpa,

      exact h (exists.intro ha hpa),
    },
  end

  example : (∃ x, p x → r) ↔ (∀ x, p x) → r :=
  begin
    split,

    {
      assume hex,
      assume hfa,

      have f : (∀ (a : α), (λ (x : α), p x → r) a → r),
      assume ha,
      assume hpar,
      apply hpar,
      exact hfa ha,

      exact exists.elim hex f,
    },

    {
      assume h,

      eapply by_cases,

      assume hfa : ∀ (x : α), p x,
      exact ⟨a, (λ hpa, h hfa)⟩,

      assume hnfa,
      have hnex : ∃ (x : α), ¬p x,
      {
        apply by_contradiction,
        assume hnex,
        have hfa : ∀ (x : α), p x,
        {
          assume ha,
          apply by_contradiction,
          assume hnpa,
          exact hnex ⟨ha, hnpa⟩,
        },
        exact hnfa hfa,
      },

      exact (
        match hnex with ⟨x, hnpx⟩ :=
          ⟨x, (λ hpx : p x, absurd hpx hnpx)⟩
        end
      ),
    },
  end

  example : (∃ x, r → p x) ↔ (r → ∃ x, p x) :=
  begin
    split,

    {
      assume hex,
      assume hr,

      have f : (∀ (a : α), (λ (x : α), r → p x) a → (∃ y : α, p  y)),
      assume ha,
      assume hrpha,
      exact exists.intro ha (hrpha hr),

      exact exists.elim hex f,
    },

    {
      assume h,

      eapply by_cases,

      assume hr : r,
      exact (
        match h hr with ⟨ha, hpa⟩ :=
          ⟨ha, (λ hr' : r, hpa)⟩
        end
      ),

      assume hnr : ¬r,
      exact ⟨a, (λ hr : r, absurd hr hnr)⟩,
    },
  end
end

-- Exercise 6
--
-- Give a calculational proof of the theorem log_mul below.
section
  variables (real : Type) [ordered_ring real]
  variables (log exp : real → real)
  variable  log_exp_eq : ∀ x, log (exp x) = x
  variable  exp_log_eq : ∀ {x}, x > 0 → exp (log x) = x
  variable  exp_pos    : ∀ x, exp x > 0
  variable  exp_add    : ∀ x y, exp (x + y) = exp x * exp y

  -- this ensures the assumptions are available in tactic proofs
  include log_exp_eq exp_log_eq exp_pos exp_add

  example (x y z : real) :
  exp (x + y + z) = exp x * exp y * exp z :=
  by rw [exp_add, exp_add]

  example (y : real) (h : y > 0)  : exp (log y) = y :=
  exp_log_eq h

  theorem log_mul {x y : real} (hx : x > 0) (hy : y > 0) :
    log (x * y) = log x + log y :=
    begin
      rw <- exp_log_eq hx,
      rw <- exp_log_eq hy,
      rw <- exp_add,
      repeat {rw log_exp_eq},
    end
end

-- Exericse 7
--
-- Prove the theorem below, using only the ring properties of ℤ enumerated in
-- Section 4.2 and the theorem sub_self.
section
  #check sub_self

  example (x : ℤ) : x * 0 = 0 :=
  begin
    -- NOTE: Is this supposed to be harder? I think that mul_zero is a property
    --       of rings.
    rw mul_zero,
  end
end


/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import data.multiset.nodup
import data.list.nat_antidiagonal

/-!
# Antidiagonals in ℕ × ℕ as multisets

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

This file defines the antidiagonals of ℕ × ℕ as multisets: the `n`-th antidiagonal is the multiset
of pairs `(i, j)` such that `i + j = n`. This is useful for polynomial multiplication and more
generally for sums going from `0` to `n`.

## Notes

This refines file `data.list.nat_antidiagonal` and is further refined by file
`data.finset.nat_antidiagonal`.
-/

namespace multiset
namespace nat

/-- The antidiagonal of a natural number `n` is
    the multiset of pairs `(i, j)` such that `i + j = n`. -/
def antidiagonal (n : ℕ) : multiset (ℕ × ℕ) :=
list.nat.antidiagonal n

/-- A pair (i, j) is contained in the antidiagonal of `n` if and only if `i + j = n`. -/
@[simp] lemma mem_antidiagonal {n : ℕ} {x : ℕ × ℕ} :
  x ∈ antidiagonal n ↔ x.1 + x.2 = n :=
by rw [antidiagonal, mem_coe, list.nat.mem_antidiagonal]

/-- The cardinality of the antidiagonal of `n` is `n+1`. -/
@[simp] lemma card_antidiagonal (n : ℕ) : (antidiagonal n).card = n+1 :=
by rw [antidiagonal, coe_card, list.nat.length_antidiagonal]

/-- The antidiagonal of `0` is the list `[(0, 0)]` -/
@[simp] lemma antidiagonal_zero : antidiagonal 0 = {(0, 0)} :=
rfl

/-- The antidiagonal of `n` does not contain duplicate entries. -/
@[simp] lemma nodup_antidiagonal (n : ℕ) : nodup (antidiagonal n) :=
coe_nodup.2 $ list.nat.nodup_antidiagonal n

@[simp] lemma antidiagonal_succ {n : ℕ} :
  antidiagonal (n + 1) = (0, n + 1) ::ₘ ((antidiagonal n).map (prod.map nat.succ id)) :=
by simp only [antidiagonal, list.nat.antidiagonal_succ, coe_map, cons_coe]

lemma antidiagonal_succ' {n : ℕ} :
  antidiagonal (n + 1) = (n + 1, 0) ::ₘ ((antidiagonal n).map (prod.map id nat.succ)) :=
by rw [antidiagonal, list.nat.antidiagonal_succ', ← coe_add, add_comm, antidiagonal, coe_map,
  coe_add, list.singleton_append, cons_coe]

lemma antidiagonal_succ_succ' {n : ℕ} :
  antidiagonal (n + 2) =
  (0, n + 2) ::ₘ (n + 2, 0) ::ₘ ((antidiagonal n).map (prod.map nat.succ nat.succ)) :=
by { rw [antidiagonal_succ, antidiagonal_succ', map_cons, map_map, prod_map], refl }

lemma map_swap_antidiagonal {n : ℕ} :
  (antidiagonal n).map prod.swap = antidiagonal n :=
by rw [antidiagonal, coe_map, list.nat.map_swap_antidiagonal, coe_reverse]

end nat
end multiset


import algebra.module.basic
import data.real.basic

/-
This is EXAM #1 for UVa CS 4501/6501, Spring
2021.

Ground rules are as follows:
- open book, notes, code, WWW
- no communication except with instructors
- don't post any part of an answer on Piazza
- use Piazza only to ask about errors or to request clarifications

The exam is due before midnight on Friday, Apr 2.

Read the following and follow the instructions.
Submit your completed lin2K_test.lean file. That's it!B
-/

/-
There are at least two important advantages
of defining a typeclass instance for a given
algebraic structure. First, if you actually
write the proofs, then you can be sure you
have made no mistakes. Second, in practical
terms, you also get a well organized set of
operations that allow you to "program in the
algebra."

This problem will evaluate your ability to
read and understand typeclass definitions
and instances. In particular, we will build
an instsance of the Lean-give "vector_space"
typeclass to enable programming in a 2-D
vector space over any field K. Vectors will
be pairs of K values. Scalars will be single
K values. The two key operations are vector
vector addition and scalar multiplication of
a vector.

More explanation follows, especially for
those students who've not had any linear
algebra. Don't worry, it's pretty easy.
-/

/-
We'll start by assuming that K is a non-empty
field.
-/

universes u
variables {K : Type u} [field K] [inhabited K]

/-
Here's the typeclass we're going to instantiate.
The two type arguments, K and K × K, specify the
types of the scalars (K) and vectors (pairs of K
values).
-/
#check vector_space K (K × K)

/-
Again, scalars of the vector space will
be values of type K; while the vectors
will be pairs of scalar values. We will
also have vector-vector addition and
scalar-vector multiplication defined
"as usual":

(k11,k12)+(k21,k22) = (k11+k21, k12+k22)
k • (k11,k12) =(k * k11, k * k12) The +
notation between two vectors is overloaded
to mean vector addition as we've defined
it here. The + operator between two scalars
is addition in the scalar field. The • is
infix for scalar vector multiplication
(smul). The * operator is multiplication
of scalars in the scalar field.
-/

/-
In a nutshell, then, this module lets
you write "2-D vector space code over
any field, K" with the standard vector
space operators of vector-vector add
and scalar vector multiplication along
with the addition and multiplication of
scalars themselves.
-/

/-
At this point the best way to understand what
follows is to jump to the end, see where we're
going, read the associated comments, and work
backwards. You can actually skip ths step on a
first readings and just use the vector-space
API that this module provides.

We are not asking you to write typeclasses and
instances on this question. Your real focus
should be on understanding (1) that creating a
typeclass instances let's you "write good code
in a given algebraic structure," (2) each of
the properties that the vector space and field
operators have to have to really have a vector
space.

Once you've understood what's in this file,
please go to the lin2k_test.file, and answer
the questions there.
-/

/-
Now please JUMP to the end of this file (look
for "JUMP"). That's where we instantiate the
vector-space API for ordered pairs of field
values with the usual addition and scaling
operations. From there you will see what we
had to build by way of operations, "helper"
instances, and proofs (all stubbed out using
sorry), in order to create the vector_space
instance for values of type K and K × K. Work
your way back up to the comment just below
and you will have finished understanding what
we've done. Then go over to lin2k_test.lean
to answer the test questions.
-/

/-
Your UPWAWRD reading has now ended, too.
-/

/-
Ordered pair addition is already overloaded
for values of type prod α β, courtesy of the
Lean library, provided that α and β themselves
have overloaded add (+) operations. The +
notation is Lean-provided for *any* type that
implements has-add. Here we use it to add
vectors in the form ordered pairs of K values.

Here is what it says in the Lean library. You
can always right click on a name and select
"go to definition" to see its definition. Try
it for has_add below. Here's what you'll find.

class has_add (α : Type u) := (add : α → α → α)
...
infix +        := has_add.add

That's the class definition *and* the + notation.
-/
#check has_add  -- right click, go to definition

/-
In addition to vector-vector addition, a
vector space also has a scalar-vector
multiplication operator. Lean defines •
as an infix notation for multiplying a
scalar by a vector (scalar on left). So,
for example, if 5 is a scalar and (2, 3)
is a vector, you can write 5 • (2, 3) to
express the "scaling" of (2, 3) by 5.

Here are details from the Lean library.
/-- Scalar multiplication operation, denoted `•` (`\bu`) -/
class has_scalar (α : Type u) (γ : Type v) := (smul : α → γ → γ)
infixr ` • `:73 := has_scalar.smul
-/
def scalar_vector_mul : K → K × K → K × K
| a (f,s) := ⟨ a * f, a * s ⟩

-- For our scalars smul will be our smul
instance : has_scalar K (K × K) := ⟨ scalar_vector_mul ⟩

/-
Rules for scalar multiplication.
- scalar multiplication by one
- scalar multiplication by produt of scalars

class mul_action (α : Type u) (β : Type v) [monoid α] extends has_scalar α β :=
(one_smul : ∀ b : β, (1 : α) • b = b)
(mul_smul : ∀ (x y : α) (b : β), (x * y) • b = x • y • b)
-/
lemma one_smul_l2  {K : Type u} [field K] [inhabited K] : ∀ v : K × K, (1 : K) • v = v := sorry
lemma mul_smul_l2 : ∀ (x y : K) (b : K × K), (x * y) • b = x • y • b := sorry
instance : mul_action K (K × K) := ⟨ one_smul_l2, mul_smul_l2 ⟩

/-
Rules for scalar multiplication:
- multiplying scalar by sum of vectors
- multiplying scalar by zero vector

class distrib_mul_action (α : Type u) (β : Type v) [monoid α] [add_monoid β]
  extends mul_action α β :=
(smul_add : ∀(r : α) (x y : β), r • (x + y) = r • x + r • y)
(smul_zero : ∀(r : α), r • (0 : β) = 0)
-/
lemma smul_add_l2 : ∀ (r : K) (x y : K × K), r • (x + y) = r • x + r • y := sorry
lemma smul_zero_l2 : ∀ (r : K), r • (0 : K × K) = 0 := sorry
instance : distrib_mul_action K (K × K) := ⟨ smul_add_l2, smul_zero_l2⟩

/-
- scalar multiplication by a sum of scalars
- scalar multiplication by scalar zero

class semimodule (R : Type u) (M : Type v) [semiring R]
  [add_comm_monoid M] extends distrib_mul_action R M :=
(add_smul : ∀(r s : R) (x : M), (r + s) • x = r • x + s • x)
(zero_smul : ∀x : M, (0 : R) • x = 0)

Note that because K (R here) is assumed to be a field
(see the top of this file), it is already equipped with
a semiring and an additive monoid structure, so we don't
need to specify those ourselves. They're already "there."
-/
lemma add_smul_l2 : ∀ (r s : K) (x : K × K), (r + s) • x = r • x + s • x := sorry
lemma zero_smul_l2 : ∀ (x : K × K), (0 : K) • x = 0 := sorry
instance semimodule_K_Kpair : semimodule K (K × K) := ⟨ add_smul_l2, zero_smul_l2 ⟩

/-
Hi! JUMP down to here from above.
-/
/-
Instantiating the vector_space API for scalars of type
K and vectors of type K × K will provide us with nice
notations for writing typechecked algebraic expressions
in this space. For examples, see linear2K_test.lean. To
see what was required to instantiate this API/typeclass,
see the comments below the live Lean code. They unpack
the definition of the vector_space typeclass from the
lean algebra library, revealing the prerequisites for
completing the job here. As you drill recursively down
through lower-level abstractions, you'll encounter new
algebraic structure names (things like "semimodule").
Now you have a good sense of what each of these classes
looks like: maybe some new data or operations, maybe
some new structuring constraints/rules/invariants.
-/
instance : vector_space K (K × K) := semimodule_K_Kpair
/-
vector_space :
  Π (R : Type u_1)                -- ring of scalars
    (M : Type u_2)                -- set of vectoids
    [_inst_1 : field R]           -- implicit
    [_inst_2 : add_comm_group M], -- implicit
  Type (max u_1 u_2) :=
    semimodule R M      -- a vector space R M is a semimodule R M
-/

/-
Yeah! We've now specified that we can treat
ordered pairs over a field K a vector space,
with the following operations.

+   vector vector addition
•   scalar vector multiplication

Now proceed to linear2K_test.lean to show that
you understand how to use this algebraic structure.
-/


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

import tactic -- imports all the Lean tactics

/-!

# Logic in Lean, example sheet 6 : "or" (∨`)

We learn about how to manipulate `P ∨ Q` in Lean.

## Tactics

You'll need to know about the tactics from the previous sheets,
and also the following tactics

* `left` and `right`
* `cases` (new functionality)

-/

-- Throughout this sheet, `P`, `Q`, `R` and `S` will denote propositions.

variables (P Q R S : Prop)

example : P → P ∨ Q :=
begin
  intro q,left,assumption,
end

example : Q → P ∨ Q :=
begin
  intro q, right,assumption,
end

example : P ∨ Q → (P → R) → (Q → R) → R :=
begin
  intros q w e,cases q,
  {apply w, assumption},
  {apply e,assumption},
end

-- symmetry of `or`
example : P ∨ Q → Q ∨ P :=
begin
  intro q,cases q with w e,right,assumption,left,assumption,
end

-- associativity of `or`
example : (P ∨ Q) ∨ R ↔ P ∨ (Q ∨ R) :=
begin
  split,intro q,cases q with w e,{cases w,{left,assumption,},{right,left,assumption,}},
  {right,right,assumption,},
  intro q,cases q,left,left,assumption,cases q,left,right, assumption,right,assumption,
end

example : (P → R) → (Q → S) → P ∨ Q → R ∨ S :=
begin
  rintro q w e,cases e,{left,apply q, assumption,},
  {right,apply w,assumption},
end

example : (P → Q) → P ∨ R → Q ∨ R :=
begin
  intros q w,cases w,left,apply q, assumption,right,assumption,
end

example : (P ↔ R) → (Q ↔ S) → (P ∨ Q ↔ R ∨ S) :=
begin
  intros q w,split,intro e,cases e,{cases q,left,apply q_mp,assumption,},{cases w,right,apply w_mp,assumption,},
  intro e,cases w,cases e,cases q,left,apply q_mpr,assumption,right,apply w_mpr,assumption,
end

-- de Morgan's laws
example : ¬ (P ∨ Q) ↔ ¬ P ∧ ¬ Q :=
begin
  split,intro q,change (P∨Q)->false at q,split,intro w,apply q,left,assumption,intro w,apply q,right,assumption,
  intro q,change (P∨Q)->false,intro w,change (P->false)∧(Q->false) at q,cases q,cases w,apply q_left,assumption,apply q_right,assumption,
end

example : ¬ (P ∧ Q) ↔ ¬ P ∨ ¬ Q :=
begin
  split,intro q,{by_cases Q,left,change (P∧ Q)->false at q,change P->false,intro w,apply q,split,assumption,assumption,right,assumption},
  {intro q,change(P∧Q)->false, intro w,by_cases P,cases q,apply q,assumption,apply q,cases w,assumption,apply h,cases w,assumption,},
end


import BrownCs22.Library.Tactics
import BrownCs22.Library.Defs

/-

(Note: we'll actually cover this in lecture 7, so it's duplicated
in `Lecture07.lean`.)

# First order logic in Lean

We were a little vague in class about what function symbols, predicate symbols,
etc. we were going to allow in our formulas.
In Lean, we need to be more precise.

Symbols in Lean must have *types*:
a predicate holds on `ℕ`, or on `ℤ`, or on some arbitrary type `T`,
but not on all at once.

We can introduce a new predicate symbol that takes one natural number argument
as follows:

-/

variable (P : ℕ → Prop)

#check P 3

/-

Notice that we don't need to write parentheses around the argument.
We can, though:

-/

#check P (3)

/-

If we want a predicate symbol that takes two arguments:

-/

variable (R : ℕ → ℕ → Prop)

#check R 3 4

-- this doesn't work!
-- #check R (3, 4)

/-

Finally, Lean has lots of predicates and function symbols built in.
Want to use `+`, `*`, `=`, all that familiar stuff? It's here already.

-/

/-

## Universal introduction

We said that to prove `∀ x : T, ...` goals, we got to introduce a new
`x : T`. The tactic for this proof rule is familiar. The pattern here
sounds a lot like implication: move something from the goal to the context.

We use the tactic `intro` again!

The `reflexivity` tactic will prove things equal to themselves.
This is a very basic property of `=`.

-/

example : ∀ x : ℤ, x = x := by
  intro x
  reflexivity

 -- Every even integer greater than two is the sum of two primes.
example : ∀ n : ℕ, Even n ∧ (n > 2) → ∃p q : ℕ, Prime p ∧ Prime q ∧ (n = p + q) := by
  intro n
  intro h_and
  sorry

/-

## Universal elimination

If we know `hall : ∀ x : T, H x`, we can conclude `H t` for any `t : T`.
We again do this just like implication elimination:
`have ht : H t := hall t`.

We can also use `apply`.

Note: I've started this example with the hypothesis `hxp` "pre-intro'ed."
Putting it left of the `:` means it is in my context at the beginning.
This saves us a line.

-/

example (hxp : ∀ x : ℕ, P x) : P 5 ∧ P 6 := by
  split_goal

  -- method 1: using `have`
  { have hP5 : P 5 := hxp 5
    assumption }

  -- method 2: using apply
  { apply hxp }

/-

## Existential introduction

To prove an `exists` goal, we provide a witness.
The tactic for giving a witness is `existsi` (for "exists introduction," creatively).

The tactic `numbers` proves silly obvious facts about explicit numbers.
(It doesn't know anything about variables, like `x`.)

-/

example : ∃ x : ℕ, x > 10 := by
  existsi 1000
  numbers

/-

## Existential elimination

This will be familiar again!
To use an `exists` hypothesis, we can use the good old `eliminate` tactic
to get a witness and a hypothesis about that witness.

-/

example (hex : ∃ x : ℕ, P x) : 0 = 0 := by
  eliminate hex with k hPk
  numbers

example (hex : ∃ x : ℕ, P x) (hall : ∀ x : ℕ, P x → P (x + 1)) :
    ∃ x : ℕ, P x ∧ P (x + 1) := by
  eliminate hex with k hPk
  have hk : P k → P (k + 1) := hall k
  have hPk1 : P (k + 1) := hk hPk
  existsi k
  split_goal
  { assumption }
  { assumption }


import mini_crush

namespace hide
-- *Introducing Inductive Types

-- **Proof Terms
#check (fun x : ℕ, x)

#check (fun x : true, x)

#check true.intro

#check (λ _ : false, true.intro)

#check (λ x : false, x)

-- **Enumerations

-- unit
inductive unit : Type
| tt

#check unit
#check unit.tt

theorem unit_singleton_verbose (x : unit) : x = unit.tt :=
by induction x; refl

theorem unit_singleton_concise : ∀ x : unit, x = unit.tt
| unit.tt := rfl

-- TODO: is cases_on like unit_ind?
-- TODO: hovering over #check gives a different type than hovering over the expression itself (which yields the "correct" answer)
#check unit.cases_on

-- empty
inductive empty : Type

-- TODO: cases vs induction?
theorem the_sky_is_falling (x : empty) : 2 + 2 = 5 :=
by cases x

#check empty.cases_on

-- empty to unit
def e2u (e : empty) : unit := match e with end

-- booleans
inductive bool : Type
| true
| false

-- bool negation
def negb : bool → bool
| bool.true  := bool.false
| bool.false := bool.true

-- TODO: negb' ? is there a version of ite that works for any type w/ 2 constructors?

-- proof that negb is its own inverse operation
theorem negb_inverse : ∀ b : bool, negb (negb b) = b
| bool.true  := rfl
| bool.false := rfl

-- proof that negb has no fixpoint
theorem negb_ineq (b : bool) : negb b ≠ b :=
by cases b; contradiction

-- **Simple Recursive Types

-- nat and some functions
inductive nat : Type
| O : nat
| S : nat → nat

def is_zero : nat → bool
| nat.O     := bool.true
| (nat.S _) := bool.false

def pred : nat → nat
| nat.O     := nat.O
| (nat.S n) := n

def plus : nat → nat → nat
| nat.O m     := m
| (nat.S n) m := nat.S (plus n m)

theorem O_plus_n : ∀ n : nat, plus nat.O n = n :=
begin
simp [plus]
end

theorem O_plus_n_crush : ∀ n : nat, plus nat.O n = n :=
by mini_crush

theorem n_plus_O : ∀ n : nat, plus n nat.O = n :=
begin
intro n,
induction n,
{refl},
simp [plus],
rw ih_1
end

theorem n_plus_O_crush : ∀ n : nat, plus n nat.O = n :=
by mini_crush

theorem s_inj : ∀ n m : nat, nat.S n = nat.S m → n = m :=
begin
intros n m succ_eq,
injection succ_eq
end

theorem s_inj_crush : ∀ n m : nat, nat.S n = nat.S m → n = m :=
by mini_crush

inductive nat_list : Type
| NNil  : nat_list
| NCons : nat → nat_list → nat_list

-- length
def nlength : nat_list → nat
| nat_list.NNil         := nat.O
| (nat_list.NCons _ ls) := nat.S (nlength ls)

-- apppend
def napp : nat_list → nat_list → nat_list
| nat_list.NNil          ls2 := ls2
| (nat_list.NCons n ls1) ls2 := nat_list.NCons n (napp ls1 ls2)

-- the length of an appended list made from ls1 and ls2 is the sum of the lengths of ls1 and ls2
theorem nlength_napp : ∀ ls1 ls2 : nat_list, nlength (napp ls1 ls2) = plus (nlength ls1) (nlength ls2) :=
begin
intros,
induction ls1; simp [nlength, plus, napp],
rw ih_1
end

theorem nlength_napp_crush : ∀ ls1 ls2 : nat_list, nlength (napp ls1 ls2) = plus (nlength ls1) (nlength ls2) :=
by mini_crush

-- binary tree
inductive nat_btree : Type
| NLeaf : nat_btree
| NNode : nat_btree → nat → nat_btree → nat_btree

def nsize : nat_btree → nat
| nat_btree.NLeaf             := nat.S nat.O
| (nat_btree.NNode tr1 _ tr2) := plus (nsize tr1) (nsize tr2)

def nsplice : nat_btree → nat_btree → nat_btree
| nat_btree.NLeaf               tr2 := nat_btree.NNode tr2 nat.O nat_btree.NLeaf
| (nat_btree.NNode tr1' n tr2') tr2 := nat_btree.NNode (nsplice tr1' tr2) n tr2'

theorem plus_assoc : ∀ n1 n2 n3 : nat, plus (plus n1 n2) n3 = plus n1 (plus n2 n3) :=
begin
intros,
induction n1; simp [plus],
rw ih_1
end

theorem nsize_nsplice : ∀ tr1 tr2 : nat_btree, nsize (nsplice tr1 tr2) = plus (nsize tr2) (nsize tr1) :=
begin
intros,
induction tr1; simp [nsize, nsplice],
simp [ih_1, ih_2, plus_assoc]
end

#check nat_btree.cases_on

-- **Parameterized Types
-- TODO: is there a way to remove type annotations nearly everywhere ala CPDT p. 50?

universe variable u

inductive list (α : Type u) : Type u
| Nil {} : list
| Cons   : α → list → list

def length {α : Type u} : list α → nat
| list.Nil        := nat.O
| (list.Cons _ l) := nat.S (length l)

def app {α : Type u} : list α → list α → list α
| list.Nil          ls2 := ls2
| (list.Cons x ls1) ls2 := list.Cons x (app ls1 ls2)

-- nearly identical to nlength_napp
theorem length_app : Π (α : Type u), ∀ ls1 ls2 :
list α, length (app ls1 ls2) = plus (length ls1) (length ls2) :=
begin
intros,
induction ls1; simp [length, plus, app],
rw ih_1
end

theorem length_app_crush : Π (α : Type u), ∀ ls1 ls2 :
list α, length (app ls1 ls2) = plus (length ls1) (length ls2) :=
by mini_crush

-- **Mutually Inductive Types

-- even and odd lists
mutual inductive even_list, odd_list
with even_list : Type
| ENil  : even_list
| ECons : nat → odd_list → even_list
with odd_list : Type
| OCons : nat → even_list → odd_list

mutual def elength, olength
with elength : even_list → nat
| even_list.ENil         := nat.O
| (even_list.ECons _ ol) := nat.S (olength ol)
with olength : odd_list → nat
| (odd_list.OCons _ el) := nat.S (elength el)

mutual def eapp, oapp
with eapp : even_list → even_list → even_list
| even_list.ENil         el2 := el2
| (even_list.ECons n ol) el2 := even_list.ECons n (oapp ol el2)
with oapp : odd_list → even_list → odd_list
| (odd_list.OCons n el') el := odd_list.OCons n (eapp el' el)

-- TODO: come back to this one
theorem elength_eapp : ∀ el1 el2 : even_list,
elength (eapp el1 el2) = plus (elength el1) (elength el2) :=
begin
intros,
induction el1; simp [elength, eapp, plus],
admit
end

-- **Reflexive Types

-- a reflexive type A includes at least one constructor that takes an argument of type B → A

-- subset of predicate logic
inductive pformula : Type
| Truth : pformula
| Falsehood : pformula
| Conjunction : pformula → pformula → pformula

-- pformula denotational semantics
def pformulaDenote : pformula → Prop
| pformula.Truth := true
| pformula.Falsehood := false
| (pformula.Conjunction f1 f2) := pformulaDenote f1 ∧ pformulaDenote f2

inductive formula : Type
| Eq : nat → nat → formula
| And : formula → formula → formula
| Forall : (nat → formula) → formula
-- variables implicitly defined using lean functions
-- e.g. ∀ x : nat, x = x is encoded as
example forall_refl : formula := formula.Forall (fun x, formula.Eq x x)

-- formula denotational semantics
def formulaDenote : formula → Prop
| (formula.Eq n1 n2)  := n1 = n2
| (formula.And f1 f2) := formulaDenote f1 ∧ formulaDenote f2
| (formula.Forall f)  := ∀ n : nat, formulaDenote (f n)

-- a trivial formula transformation
def swapper : formula → formula
| (formula.Eq n1 n2)  := formula.Eq n2 n1
| (formula.And f1 f2) := formula.And (swapper f2) (swapper f1)
| (formula.Forall f)  := formula.Forall (fun n, swapper (f n))

theorem swapper_preserves_truth (f : formula) : formulaDenote f → formulaDenote (swapper f) :=
by mini_crush

#check formula.cases_on

/-
inductive term : Type
| App : term → term → term
| Abs : (term → term) → term
open term

def uhoh (t : term) : term
| (Abs f) := f t
| _ := t
-/

-- **An Interlude on Induction Principles

-- TODO: equiv to nat_ind? nat_rect? nat_rec?
#check @nat.rec
#check @nat.cases_on

-- skipping the rest of this section for now

-- **Nested Inductive Types

-- tree with arbitrary finite branching
inductive nat_tree : Type
| NNode' : nat → list nat_tree → nat_tree

#check nat_tree.rec

section All
variable T : Type
variable P : T → Prop

def All : list T → Prop
| list.Nil        := true
| (list.Cons h t) := P h ∧ All t

end All

#print true
#print ∧
#print and

section nat_tree_ind'
variable P : nat_tree → Prop

-- TODO

end nat_tree_ind'

-- TODO: rest of 3.8

-- **Manual Proofs About Constructors

-- todo
theorem true_neq_false : tt ≠ ff :=
begin
change ¬(tt = ff), -- TODO: replace this with something else?
change (tt = ff) → false,
intro H,
admit -- TODO: need a version of ite that takes any type w/ 2 constructors
end

theorem S_inj' : ∀ n m : nat, nat.S n = nat.S m → n = m :=
begin
intros n m H,
change (pred (nat.S n) = pred (nat.S m)),
rw H
end

end hide


-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que los números reales tienen la siguente
-- propiedad
--    (a * b) * c = b * (a * c)
-- ---------------------------------------------------------------------

import data.real.basic

-- 1ª demostración
-- ===============

example (a b c : ℝ) : (a * b) * c = b * (a * c) :=
begin
  rw mul_comm a b,
  rw mul_assoc b a c,
end

-- Comentarios:
-- 1. Al colocar el cursor sobre el nombre de un lema se ve su enunciado.
-- 2. Para completar el nombre de un lema basta escribir parte de su
--    nombre y completar con S-SPC (es decir, simultáneamente las teclas
--    de mayúscula y la de espacio).
-- 3. los lemas usados son
--    + mul_com   : (∀ a b : G),   a * b = b * a
--    + mul_assoc : (∀ a b c : G), (a * b) * c = a * (b * c)
-- 4. La táctica (rw e) (ver https://bit.ly/3eUxbjD) reescribe una
--    expresión usando la ecuación e.

-- El desarrollo de la prueba es:
--
--    inicio
--       a b c : ℝ
--       ⊢ (a * b) * c = b * (a * c)
--    rw mul_comm a b,
--       a b c : ℝ
--       ⊢ (a * b) * c = b * (a * c)
--    rw mul_assoc b a c
--       no goals

-- 2ª demostración
-- ===============

example
  (a b c : ℝ)
  : (a * b) * c = b * (a * c) :=
calc
  (a * b) * c = (b * a) * c : by rw mul_comm a b
          ... = b * (a * c) : by rw mul_assoc b a c

-- Comentario: El entorno calc (ver https://bit.ly/2NRxiAU) permite
-- escribir demostraciones ecuacionales.

-- 3ª demostración
-- ===============

example (a b c : ℝ) : (a * b) * c = b * (a * c) :=
by ring

-- Comentario: La táctica ring demuestra ecuaciones aplicando las
-- propiedades de anillos.