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
  -- $\frac a {b + c} + \frac b {a + c} + \frac c {a + b}$ is the arithmetic mean
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (a + b + c) / 3, from
    by {norm_num,ring},
  -- $\frac 1 {b + c} + \frac 1 {a + c} + \frac 1 {a + b}$ is the harmonic mean
  have h2 : (1 / (b + c)) + (1 / (a + c)) + (1 / (a + b)) = 3 / ((b + c) + (a + c) + (a + b)), from
    by {norm_num,ring},
  -- $\frac 1 {b + c} + \frac 1 {a + c} + \frac 1 {a + b}$ is the harmonic mean
  have h3 : (1 / (b + c)) + (1 / (a + c)) + (1 / (a + b)) = 3 / ((b + c) + (a + c) + (a + b)), from
    by {norm_num,ring},
  -- $\frac a {b + c} + \frac b {a + c} + \frac c {a + b}$ is the arithmetic mean
  have h4 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (a + b + c) / 3, from
    by {norm_num,ring},
  -- $\frac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
  have h5 : (a + b + c) / ((b + c) + (a + c) + (a + b)) = 1 / 2, from
    by {norm_num,ring},
  -- $\frac {9 \paren {a + b + c} } {\paren {b + c} + \paren {a + c} + \paren {a + b} }$
  have h6 : 9 * (a + b + c) / ((b + c) + (a + c) + (a + b)) = 9 / 2, from
    by {norm_num,ring},
  -- $\frac {\frac 1 {b + c} + \frac 1 {a + c} + \frac 1 {a + b} } 3$
  have h7 : (1 / (b + c)) + (1 / (a + c)) + (1 / (a + b)) / 3 = 3 / ((b + c) + (a + c) + (a + b)), from
    by {norm_num,ring},
  -- $\frac {a + b + c} {b + c} + \frac {a + b + c} {a + c} + \frac {a + b + c} {a + b}$
  have h8 : (a + b + c) / (b + c) + (a + b + c) / (a + c) + (a + b + c) / (a + b) = 9 / 2, from
    by {norm_num,ring},
  -- $\frac a {b + c} + \frac b {a + c} + \frac c {a + b} \ge \dfrac 3 2$
  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ 3 / 2, from
    by {rw [← h1, ← h8], apply arithmetic_mean_is_never_less_than_harmonic_mean, norm_num, ring, exact hb, exact 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
  -- adding a $3$
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = ((a + b + c) / (b + c)) + ((a + b + c) / (a + c)) + ((a + b + c) / (a + b)), from
    by rw add_three,
  -- as $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
  have h2 : ((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 rw [div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same,div_add_div_same],
  -- dividing by $3 \paren {a + b + c}$
  have h3 : ((a + b + c) / (b + c)) + ((a + b + c) / (a + c)) + ((a + b + c) / (a + b)) ≥ (3 / ((b + c) + (a + c) + (a + b))), from
    by rw div_mul_div_same h1,
  -- These are the arithmetic mean and the harmonic mean of $\dfrac 1 {b + c}$, $\dfrac 1 {a + c}$ and $\dfrac 1 {a + b}$.
  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from
    by {rw ← h1, apply arithmetic_mean_is_never_less_than_harmonic_mean, exact h3}
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
  -- $a$, $b$ and $c$ are positive real numbers
  assume (ha : 0 < a) (hb : 0 < b) (hc : 0 < c),

  -- Then:
  -- :$\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$
  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from
  begin
    -- $\leadstoandfrom$
    have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / 2) * (a + b + c) / ((b + c) + (a + c) + (a + b)), from
    begin
      -- as $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
      have h1 : (a + b + c) / ((b + c) + (a + c) + (a + b)) = (1 / 2), from by {
        have h1 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a + b + c), from by ring,
        have h2 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a + b + c), from by ring,
        show ((a + b + c) / ((b + c) + (a + c) + (a + b))) = (1 / 2), from by {
          have h3 : ((a + b + c) * ((b + c) + (a + c) + (a + b))) = ((a + b + c) * (a + b + c)) + ((a + b + c) * (a + c)) + ((a + b + c) * (b + c)), from by ring,
          have h4 : ((a + b + c) * ((b + c) + (a + c) + (a + b))) = ((a + b + c) * (a + b + c)) + ((a + b + c) * (a + c)) + ((a + b + c) * (b + c)), from by ring,
          have h5 : ((a + b + c) * ((b + c) + (a + c) + (a + b))) = ((a + b + c) * (a + b + c)) + ((a + b + c) * (a + c)) + ((a + b + c) * (b + c)), from by ring,
          have h6 : ((a + b + c) * ((b + c) + (a + c) + (a + b))) = ((a + b + c) * (a + b + c)) + ((a + b + c) * (a + c)) + ((a + b + c) * (b + c)), from by ring,
          have h7 : ((a + b + c) * ((b + c) + (a + c) + (a + b))) = ((a + b + c) * (a + b + c)) + ((a + b + c) * (a + c)) + ((a + b + c) * (b + c)), from by ring,
          have h8 : ((a + b + c) * ((b + c) + (a + c) + (a + b))) = ((a + b + c) * (a + b + c)) + ((a + b + c) * (a + c)) + ((a + b + c) * (b + c)), from by ring,
          rw [h3,h7,h8,div_mul_cancel _ (ne.symm (ne_of_lt ha))],
          rw [h4,h6,h8,div_mul_cancel _ (ne.symm (ne_of_lt hb))],
          rw [h5,h6,h7,div_mul_cancel _ (ne.symm (ne_of_lt hc))],
          ring,
        },
      },
      -- $\leadstoandfrom$
      have h2 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / 2) * (a + b + c) / ((b + c) + (a + c) + (a + b)), from by {
        rw h1,
        have h3 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / 2) * (1 / 2), from by linarith,
        rw mul_comm (9 / 2) (1 / 2),
        exact h3,
      },
      -- $\leadstoandfrom$
      have h3 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / 2) * (1 / 2), from by linarith,
      rw mul_comm (9 / 2) (1 / 2),
      exact h3,
    },

    -- 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.
    have h2 : (9 / 2) * (a + b + c) / ((b + c) + (a + c) + (a + b)) ≥ (9 / 2) * (3 / ((b + c) + (a + c) + (a + b))), from by {
      have h3 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a + b + c), from by ring,
      have h4 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a + b + c), from by ring,
      have h5 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a + b + c), from by ring,
      have h6 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a + b + c), from by ring,
      have h7 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a + b + c), from by ring,
      have h8 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a + b + c), from by ring,
      have h9 : ((b + c) + (a + c) + (a + b)) * (a + b + c) = (b + c) * (a + b + c) + (a + c) * (a + b + c) + (a + b) * (a
end --Needs more than 2000 tokens!

--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
  -- Rearranging and adding $3$ gives:
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from by {
    rw [add_div,add_div,add_div,add_div,add_div,add_div,add_div],
    have h2 : a + b + c > 0, from by {rw ← add_assoc, rw add_comm b c, rw add_assoc, rw add_comm a (b + c), rw add_assoc, rw add_comm c a, rw add_assoc, rw add_comm b c, rw add_assoc, rw add_comm a (b + c), rw add_assoc, rw add_comm c a, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, ring},
    rw ← div_div_eq_div_mul,
    rw div_self h2,
    rw mul_one,
    rw mul_comm,
    rw mul_one,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw add_mul,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ← add_assoc,
    rw ←
end --Needs more than 2000 tokens!

--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
  -- adding $3$ gives:
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / 2), from by {
    have h2 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (a / (b + c)) + (b / (a + c)) + (c / (a + b)) + 3, from by {
      rw ← add_assoc (c / (a + b)) (b / (a + c)) (a / (b + c)),
      rw add_comm 3 (c / (a + b)), rw ← add_assoc (b / (a + c)) (a / (b + c)) 3,
      rw ← add_assoc (a / (b + c)) 3 (b / (a + c)), rw ← add_assoc 3 (b / (a + c)) (a / (b + c)),
      repeat {rw ← add_assoc},
    },
    have h3 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) + 3 = (a / (b + c)) + 3 + (b / (a + c)) + (c / (a + b)), from by {
      rw ← add_assoc (c / (a + b)) (b / (a + c)) (a / (b + c)),
      rw add_comm 3 (c / (a + b)), rw ← add_assoc (b / (a + c)) (a / (b + c)) 3,
      rw ← add_assoc (a / (b + c)) 3 (b / (a + c)), rw ← add_assoc 3 (b / (a + c)) (a / (b + c)),
      repeat {rw ← add_assoc},
    },
    have h4 : (a / (b + c)) + 3 + (b / (a + c)) + (c / (a + b)) = 3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b)), from by {
      rw ← add_assoc 3 (c / (a + b)) ((a / (b + c)) + (b / (a + c))),
      rw ← add_assoc 3 ((a / (b + c)) + (b / (a + c))) (c / (a + b)),
      repeat {rw ← add_assoc},
    },
    have h5 : 3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = 3 + ((a / (b + c)) + (b / (a + c)) + (c / (a + b))), from by {
      rw ← add_assoc 3 (c / (a + b)) ((a / (b + c)) + (b / (a + c))),
      rw ← add_assoc 3 ((a / (b + c)) + (b / (a + c))) (c / (a + b)),
      repeat {rw ← add_assoc},
    },
    have h6 : 3 + ((a / (b + c)) + (b / (a + c)) + (c / (a + b))) = (3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b))), from by {
      rw ← add_assoc 3 (c / (a + b)) ((a / (b + c)) + (b / (a + c))),
      rw ← add_assoc 3 ((a / (b + c)) + (b / (a + c))) (c / (a + b)),
      repeat {rw ← add_assoc},
    },
    have h7 : 3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b))) + 0, from by {
      rw ← add_assoc (c / (a + b)) (b / (a + c)) (a / (b + c)),
      rw add_comm 3 (c / (a + b)), rw ← add_assoc (b / (a + c)) (a / (b + c)) 3,
      rw ← add_assoc (a / (b + c)) 3 (b / (a + c)), rw ← add_assoc 3 (b / (a + c)) (a / (b + c)),
      repeat {rw ← add_assoc},
    },
    have h8 : (3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b))) + 0 = (3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b))) + 3*0, from by {
      rw ← add_assoc (c / (a + b)) (b / (a + c)) (a / (b + c)),
      rw add_comm 3 (c / (a + b)), rw ← add_assoc (b / (a + c)) (a / (b + c)) 3,
      rw ← add_assoc (a / (b + c)) 3 (b / (a + c)), rw ← add_assoc 3 (b / (a + c)) (a / (b + c)),
      repeat {rw ← add_assoc},
    },
    have h9 : (3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b))) + 3*0 = (3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b))) + 3*1, from by {
      rw ← add_assoc (c / (a + b)) (b / (a + c)) (a / (b + c)),
      rw add_comm 3 (c / (a + b)), rw ← add_assoc (b / (a + c)) (a / (b + c)) 3,
      rw ← add_assoc (a / (b + c)) 3 (b / (a + c)), rw ← add_assoc 3 (b / (a + c)) (a / (b + c)),
      repeat {rw ← add_assoc},
    },
    have h10 : (3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b))) + 3*1 = 3 + (a / (b + c)) + (b / (a + c)) + (c / (a + b)) + 3, from by {
      rw ← add_assoc (c / (a + b)) (b / (a + c)) (a / (b + c)),
      rw add_comm 3 (c / (a + b)), rw ← add_assoc (b / (a + c)) (a / (b + c)) 3,
      rw ← add_assoc (a / (b + c)) 3 (b / (a + c)), rw ← add_assoc 3 (b / (a + c)) (a / (b + c)),
      repeat {rw ← add_assoc},
    },
    show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / 2), from eq.trans h2 (eq.trans h3 (eq.trans h4 (eq.trans h5 (eq.trans h6 (eq.trans h7 (eq.trans h8 h9))))))
  },

  -- as $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
  have h2 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (9 / ((b + c) + (a + c) + (a + b))), from by {
    have h3 : (a + b + c
end --Needs more than 2000 tokens!

--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
  -- By adding $3$
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ ((a + b + c) / (b + c)) + ((a + b + c) / (a + c)) + ((a + b + c) / (a + b)),
    from by {rw [add_halves,add_halves,add_halves, add_halves,add_halves,add_halves], ring,},
  -- as $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
  have h2 : ((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 {rw add_halves, ring,},
  -- dividing by $3 \paren {a + b + c}$
  have h3 : (9 * (a + b + c)) / ((b + c) + (a + c) + (a + b)) ≥ ((1 / (b + c)) + (1 / (a + c)) + (1 / (a + b))) / 3,
    from by {rw mul_assoc, apply div_le_div_of_le_of_pos h2, ring,},
  -- These are the arithmetic mean and the harmonic mean of $\dfrac 1 {b + c}$, $\dfrac 1 {a + c}$ and $\dfrac 1 {a + b}$.
  have h4 : ((1 / (b + c)) + (1 / (a + c)) + (1 / (a + b))) / 3 ≥ (3 / ((b + c) + (a + c) + (a + b))),
    from by {apply arithmetic_mean_ge_harmonic_mean,},
  have h5 : ((3 : ℝ) / ((b + c) + (a + c) + (a + b))) ≥ (3 / 2),
    from by {apply div_le_div_of_le_of_pos,ring,},
  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from by {
    apply le_trans,
    apply h1,
    apply le_trans,
    apply h3,
    apply le_trans,
    apply h4,
    apply h5,
  }
end

--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$
  have h1 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from
    by {
      -- by adding $3$
      have h2 : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (1 / (b + c)) + (1 / (a + c)) + (1 / (a + b)),
        from by {rw [div_eq_mul_inv,div_eq_mul_inv,div_eq_mul_inv,div_eq_mul_inv,div_eq_mul_inv], exact add_le_add (le_div_iff_mul_le'.mp hb) (le_div_iff_mul_le'.mp hc) (le_div_iff_mul_le'.mp hc) (le_div_iff_mul_le'.mp ha) (le_div_iff_mul_le'.mp ha) (le_div_iff_mul_le'.mp hb), },
      -- as $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
      have h3 : (1 / (b + c)) + (1 / (a + c)) + (1 / (a + b)) ≥ (3 / (2 * (b + c + a))),
        from by {rw [div_eq_mul_inv,div_eq_mul_inv,div_eq_mul_inv,div_eq_mul_inv,div_eq_mul_inv,div_eq_mul_inv], rw [← mul_assoc,← mul_assoc,← mul_assoc], rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, ring, },
      -- dividing by $3 \paren {a + b + c}$
      have h4 : (3 / (2 * (b + c + a))) ≥ (3 / ((b + c) + (a + c) + (a + b))),
        from by {rw [← div_eq_mul_inv,← div_eq_mul_inv,← div_eq_mul_inv,← div_eq_mul_inv], rw [← mul_assoc,← mul_assoc,← mul_assoc,← mul_assoc,← mul_assoc,← mul_assoc,← mul_assoc,← mul_assoc], rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, rw ← add_assoc, ring, exact mul_le_mul_left ha (le_div_iff_mul_le'.mp hb) (le_div_iff_mul_le'.mp hc) (le_div_iff_mul_le'.mp ha) (le_div_iff_mul_le'.mp hb) (le_div_iff_mul_le'.mp hc), },
      -- 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.
      exact arithmetic_mean_never_less_harmonic_mean h2 h4,
    },
  -- $\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$
  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from h1,
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
  have habc : 0 < a + b + c, from by {apply add_pos ha, apply add_pos hb, apply add_pos hc},
  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) :
  begin
    rw [← add_div_right ha, ← add_div_right hb, ← add_div_right hc],
    ring
  end
  ... ≥ (9 * (a + b + c)) / (2 * (b + c) + 2 * (a + c) + 2 * (a + b)) :
  begin
    apply div_le_div_of_le_of_pos (add_le_add (add_le_add (mul_le_mul_of_nonneg_left (le_of_lt habc) (le_of_lt (add_pos hb hc)))
      (mul_le_mul_of_nonneg_left (le_of_lt habc) (le_of_lt (add_pos ha hc))))
      (mul_le_mul_of_nonneg_left (le_of_lt habc) (le_of_lt (add_pos ha hb)))),
    show (2 * (b + c) + 2 * (a + c) + 2 * (a + b)) > 0, from by {apply add_pos, apply add_pos, apply add_pos},
    rw [← mul_assoc, ← add_assoc, ← add_assoc, ← add_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc],
    rw [← mul_assoc, ← add_assoc, ← add_assoc, ← add_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc],
    apply add_le_add (add_le_add (mul_le_mul_of_nonneg_left (le_of_lt habc) (le_of_lt (add_pos hb hc)))
      (mul_le_mul_of_nonneg_left (le_of_lt habc) (le_of_lt (add_pos ha hc))))
      (mul_le_mul_of_nonneg_left (le_of_lt habc) (le_of_lt (add_pos ha hb))),
  end
  ... ≥ (3 * ((1 / (b + c)) + (1 / (a + c)) + (1 / (a + b)))) / (b + c + a + c + a + b) :
  begin
    apply div_le_div_of_le_of_pos (add_le_add (add_le_add (mul_le_mul_of_nonneg_left (le_of_lt (add_pos hb hc)) (le_of_lt (add_pos ha hc)))
      (mul_le_mul_of_nonneg_left (le_of_lt (add_pos ha hc)) (le_of_lt (add_pos ha hb))))
      (mul_le_mul_of_nonneg_left (le_of_lt (add_pos ha hb)) (le_of_lt (add_pos hb hc)))),
    show (b + c + a + c + a + b) > 0, from by {apply add_pos, apply add_pos, apply add_pos},
    rw [← mul_assoc, ← add_assoc, ← add_assoc, ← add_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc],
    rw [← mul_assoc, ← add_assoc, ← add_assoc, ← add_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc, ← mul_assoc],
    apply add_le_add (add_le_add (mul_le_mul_of_nonneg_left (le_of_lt (add_pos hb hc)) (le_of_lt (add_pos ha hc)))
      (mul_le_mul_of_nonneg_left (le_of_lt (add_pos ha hc)) (le_of_lt (add_pos ha hb))))
      (mul_le_mul_of_nonneg_left (le_of_lt (add_pos ha hb)) (le_of_lt (add_pos hb hc))),
  end
  ... = 3 * ((1 / (b + c)) + (1 / (a + c)) + (1 / (a + b))) : by rw [← add_div_right ha, ← add_div_right hb, ← add_div_right hc]
  ... ≥ 3 * (2 / 3) : by apply arithmetic_mean_is_never_less_than_harmonic_mean
  ... = 3 : by rw [← mul_assoc, mul_comm 3 2, ← mul_assoc, mul_comm 3 2, mul_assoc],
end

/- 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 {split,apply set.subset_of_mem_powerset,exact hA,apply set.subset_of_mem_powerset,exact hB},
  -- Then $(A ∩ B) ⊆ A$, by intersection of set is a subset
  have h2 : (A ∩ B) ⊆ A, from by apply set.inter_subset_left,
  -- Then $(A ∩ B) ⊆ S$, by subset relation is transitive
  have h3 : (A ∩ B) ⊆ S, from by {apply set.subset.trans h2 h1.left},
  -- Hence $(A ∩ B) ∈  𝒫 S$, by power set definition
  show (A ∩ B) ∈  𝒫 S, from by {apply set.mem_powerset h3},
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 rw sq
  -- distributive property of multiplication over addition gives:
  ... = x*(x+y) + y*(x+y) : by rw add_mul
  -- applying the above property further gives:
  ... = x*x + x*y + y*x + y*y : by {rw [mul_comm x (x+y),mul_comm y (x+y)], rw [add_mul,add_mul], ring}
  -- rearranging the terms using commutativity and adding gives:
  ... = x^2 + 2*x*y + y^2 : by {repeat {rw ← sq}, rw mul_comm y x, 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 {
    assume a b : G, use a⁻¹ * b, obviously, },
  have h2 : ∀ a b : G, ∃! y : G, y * a = b, from by {
    assume a b : G, use b * a⁻¹, obviously, },

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

  -- 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 assume a :G,
    exists_unique.unique (h3 a) (classical.some_spec (exists_unique.exists (h3 a)))
    (mul_one a),
  have h6 : ∀ a : G, classical.some (h4 a).exists = (1 : G), from assume a : G,
    exists_unique.unique (h4 a) (classical.some_spec (exists_unique.exists (h4 a))) (one_mul a),

  show ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a, from by {
    use (1 : G),
    have h7 : ∀ e : G, (∀ a : G, e * a = a ∧ a * e = a) → e = 1, from by {
      assume (e : G) (hident : ∀ a : G, e * a = a ∧ a * e = a),
      have h8 : ∀ a : G, e = classical.some (h3 a).exists, from assume (a : G),
        exists_unique.unique (h3 a) (hident a).right
        (classical.some_spec (exists_unique.exists (h3 a))),
      have h9 : ∀ a : G, e = classical.some (h4 a).exists, from assume (a : G),
        exists_unique.unique (h4 a) (hident a).left
        (classical.some_spec (exists_unique.exists (h4 a))),
      show e = (1 : G), from eq.trans (h9 e) (h6 _),
    },
    exact ⟨by obviously, h7⟩,
  }
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)-/


import data.nat.basic
import combinatorics.simple_graph.clique
import tactic.core
import data.finset.basic
import combinatorics.simple_graph.basic
import misc_finset

open finset nat
namespace simple_graph

-- extremal graph theory studies finite graphs and we do a lot of counting so I naively thought I should
-- prove some lemmas about (sub)graphs and edge_finsets..

-- main new def below "G.is_far H s" if by deleting at most s edges from G we obtain a subgraph of H

section fedges
variables {t n : ℕ}
variables {α : Type*} [fintype α][nonempty α][decidable_eq α]
{G H : simple_graph α}[decidable_rel G.adj][decidable_rel H.adj]

-- G is a subgraph of H iff G.edge_finset is subset of H.edge_finset
lemma subgraph_edge_subset : G ≤ H ↔ G.edge_finset ⊆ H.edge_finset:=
begin
  split,{ intro gh, intros e he, obtain ⟨x,y⟩:=e, simp only [mem_edge_finset] at *, exact gh he},
  { intro gh,intros x y h, have :⟦(x,y)⟧∈ G.edge_set:=h, rw [← mem_edge_finset] at this,
  have:= gh this, rwa mem_edge_finset at this,},
end

-- graphs (on same vertex set) are equal iff edge_finsets are equal
lemma eq_iff_edges_eq   : G = H ↔ G.edge_finset = H.edge_finset:=
begin
  split, {intro eq, exact subset_antisymm (subgraph_edge_subset.mp (le_of_eq eq)) (subgraph_edge_subset.mp (le_of_eq eq.symm))},
  {intro eq, exact le_antisymm (subgraph_edge_subset.mpr (subset_of_eq eq)) (subgraph_edge_subset.mpr (subset_of_eq eq.symm))},
end

-- if G=H (finite) graph they have the same number of edges..
lemma eq_imp_edges_card_eq   : G=H → G.edge_finset.card = H.edge_finset.card:=
begin intro h,
  rwa eq_iff_edges_eq.mp h,
end

-- a subgraph of the same size or larger is the same graph (... everything is finite)
lemma edge_eq_sub_imp_eq (hs: G≤ H) (hc: H.edge_finset.card ≤ G.edge_finset.card): G = H
:=eq_iff_edges_eq.mpr  (finset.eq_of_subset_of_card_le (subgraph_edge_subset.mp hs) hc)

-- the empty graph has no edges
lemma empty_has_no_edges :(⊥ : simple_graph α).edge_finset =∅:=
begin
  ext, obtain ⟨x,y⟩:=a, rw mem_edge_finset, simp only [not_mem_empty, iff_false],
  intro h, assumption,
end

-- a graph is the empty graph iff it has no edges
lemma empty_iff_edge_empty  : G = ⊥  ↔ G.edge_finset=∅
:= by rwa [eq_iff_edges_eq, empty_has_no_edges]

-- if G is not the empty graph there exist a pair of distinct adjacent vertices
lemma edge_of_not_empty : G ≠ ⊥ → ∃v:α,∃w:α, v≠ w ∧ G.adj v w:=
begin
  contrapose,intro h,push_neg at h, push_neg, ext,rw bot_adj,specialize h x x_1,
  by_cases h':x=x_1, simp only [*, G.irrefl], have:= (h h'), tauto,
end

-- if G is 2-clique free then it is empty
lemma two_clique_free_imp_empty  : G.clique_free 2 → G = ⊥:=
begin
  intros h ,  contrapose h, obtain ⟨v,w,had⟩:=edge_of_not_empty h,
  rw clique_free,push_neg, use {v,w}, split, {tidy}, {exact card_doubleton had.1},
end

-- meet of two graphs has edges given by intersection
lemma meet_edges_eq {G H :simple_graph α} [decidable_rel G.adj][decidable_rel H.adj] : (G⊓H).edge_finset =G.edge_finset ∩ H.edge_finset:=
begin
  ext,simp only [mem_edge_finset, mem_inter], induction a,{refl},{refl},
end

-- join of two graphs has edges given by union
lemma join_edges_eq {G H :simple_graph α} [decidable_rel G.adj][decidable_rel H.adj] : (G ⊔ H).edge_finset =G.edge_finset ∪ H.edge_finset:=
begin
  ext,simp only [mem_edge_finset, mem_union], induction a,{refl},{refl},
end

-- edge sets are disjoint iff meet is empty graph
lemma disjoint_edges_iff_meet_empty {G H :simple_graph α} [decidable_rel G.adj][decidable_rel H.adj] : disjoint G.edge_finset H.edge_finset ↔  G ⊓ H = ⊥:=
begin
  rw [empty_iff_edge_empty, meet_edges_eq], exact disjoint_iff,
end

--if G and H meet in ⊥ then the card of their edge sets adds
lemma card_edges_add_of_meet_empty {G H :simple_graph α} [decidable_rel G.adj][decidable_rel H.adj] : G ⊓ H = ⊥ →
(G ⊔ H).edge_finset.card= G.edge_finset.card+ H.edge_finset.card:=
begin
  rw [← disjoint_edges_iff_meet_empty, join_edges_eq], intros h, exact card_disjoint_union h,
end

-- the subgraph formed by deleting edges (from edge_finset)
@[ext]
def del_fedges (G:simple_graph α) (S: finset (sym2 α))[decidable_rel G.adj]  :simple_graph α :={
adj:= G.adj \ sym2.to_rel S,
symm := λ a b, by simp [adj_comm, sym2.eq_swap] }

--deleting all the edges in H from G is G\H
lemma del_fedges_is_sdiff  (G H:simple_graph α) (S: finset (sym2 α))[decidable_rel G.adj][decidable_rel H.adj] :
 G.del_fedges H.edge_finset =G\H:=
begin
  ext,simp only [del_fedges, sdiff_adj, set.coe_to_finset, pi.sdiff_apply, sym2.to_rel_prop, mem_edge_set],
  refl,
end

-- now introduce a simple version of distance between graphs
-- G.is_far s H iff there exists a finset of at most s edges such that G-S is a subgraph of H

def is_far (G H :simple_graph α) (s : ℕ) [decidable_rel G.adj][decidable_rel H.adj]
:= ∃S:finset (sym2 α), ((G.del_fedges S) ≤ H) ∧ (S.card ≤ s)

lemma is_far_le (G H :simple_graph α) {s t : ℕ} (h:s≤t) [decidable_rel G.adj][decidable_rel H.adj]:
G.is_far H s → G.is_far H t:=
begin
  intro h1, obtain ⟨S,hS1,hS2⟩:=h1,exact ⟨S,hS1,le_trans hS2 h⟩,
end

lemma is_far_trivial (G H :simple_graph α) (s : ℕ) [decidable_rel G.adj][decidable_rel H.adj]:
(G.edge_finset.card ≤ s) → G.is_far H s:=
begin
  intro h,  refine ⟨G.edge_finset,_,h⟩, rw del_fedges_is_sdiff, simp only [_root_.sdiff_self, bot_le],
  exact G.edge_finset,
end

end fedges

end simple_graph


/-
Copyright (c) 2022 Anne Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Junyan Xu, Anne Baanen
-/
import linear_algebra.linear_independent
import ring_theory.localization.fraction_ring
import ring_theory.localization.integer

/-!
# Modules / vector spaces over localizations / fraction fields

This file contains some results about vector spaces over the field of fractions of a ring.

## Main results

 * `linear_independent.localization`: `b` is linear independent over a localization of `R`
   if it is linear independent over `R` itself
 * `basis.localization`: promote an `R`-basis `b` to an `Rₛ`-basis,
   where `Rₛ` is a localization of `R`
 * `linear_independent.iff_fraction_ring`: `b` is linear independent over `R` iff it is
   linear independent over `Frac(R)`
-/

open_locale big_operators
open_locale non_zero_divisors

section localization

variables {R : Type*} (Rₛ : Type*) [comm_ring R] [comm_ring Rₛ] [algebra R Rₛ]
variables (S : submonoid R) [hT : is_localization S Rₛ]

include hT

section add_comm_monoid
variables {M : Type*} [add_comm_monoid M] [module R M] [module Rₛ M] [is_scalar_tower R Rₛ M]

lemma linear_independent.localization {ι : Type*} {b : ι → M} (hli : linear_independent R b) :
  linear_independent Rₛ b :=
begin
  rw linear_independent_iff' at ⊢ hli,
  intros s g hg i hi,
  choose a g' hg' using is_localization.exist_integer_multiples S s g,
  letI := λ i, classical.prop_decidable (i ∈ s),
  specialize hli s (λ i, if hi : i ∈ s then g' i hi else 0) _ i hi,
  { rw [← @smul_zero _ M _ _ _ (a : R), ← hg, finset.smul_sum],
    refine finset.sum_congr rfl (λ i hi, _),
    dsimp only,
    rw [dif_pos hi, ← is_scalar_tower.algebra_map_smul Rₛ, hg' i hi, smul_assoc],
    apply_instance },
  refine ((is_localization.map_units Rₛ a).mul_right_eq_zero).mp _,
  rw [← algebra.smul_def, ← map_zero (algebra_map R Rₛ), ← hli],
  simp [hi, hg']
end
end add_comm_monoid

section add_comm_group
variables {M : Type*} [add_comm_group M] [module R M] [module Rₛ M] [is_scalar_tower R Rₛ M]

/-- Promote a basis for `M` over `R` to a basis for `M` over the localization `Rₛ` -/
noncomputable def basis.localization {ι : Type*} (b : basis ι R M) : basis ι Rₛ M :=
basis.mk (b.linear_independent.localization Rₛ S) $
by { rw [← @submodule.restrict_scalars_eq_top_iff Rₛ R, eq_top_iff, ← b.span_eq],
     apply submodule.span_le_restrict_scalars }

end add_comm_group

end localization

section fraction_ring

variables (R K : Type*) [comm_ring R] [field K] [algebra R K] [is_fraction_ring R K]
variables {V : Type*} [add_comm_group V] [module R V] [module K V] [is_scalar_tower R K V]

lemma linear_independent.iff_fraction_ring {ι : Type*} {b : ι → V} :
  linear_independent R b ↔ linear_independent K b :=
⟨linear_independent.localization K (R⁰),
 linear_independent.restrict_scalars (smul_left_injective R one_ne_zero)⟩

end fraction_ring


/-
Copyright (c) 2022 Anatole Dedecker. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Anatole Dedecker
-/
import ring_theory.simple_module
import topology.algebra.module.basic

/-!
# The kernel of a linear function is closed or dense

In this file we prove (`linear_map.is_closed_or_dense_ker`) that the kernel of a linear function `f
: M →ₗ[R] N` is either closed or dense in `M` provided that `N` is a simple module over `R`. This
applies, e.g., to the case when `R = N` is a division ring.
-/

universes u v w

variables {R : Type u} {M : Type v} {N : Type w}
  [ring R] [topological_space R]
  [topological_space M] [add_comm_group M] [add_comm_group N]
  [module R M] [has_continuous_smul R M] [module R N]
  [has_continuous_add M] [is_simple_module R N]

/-- The kernel of a linear map taking values in a simple module over the base ring is closed or
dense. Applies, e.g., to the case when `R = N` is a division ring. -/
lemma linear_map.is_closed_or_dense_ker (l : M →ₗ[R] N) :
  is_closed (l.ker : set M) ∨ dense (l.ker : set M) :=
begin
  rcases l.surjective_or_eq_zero with (hl|rfl),
  { exact l.ker.is_closed_or_dense_of_is_coatom (linear_map.is_coatom_ker_of_surjective hl) },
  { rw linear_map.ker_zero,
    left,
    exact is_closed_univ },
end


/-
Copyright (c) 2019 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import analysis.specific_limits.basic
import data.rat.denumerable
import data.set.pointwise.interval
import set_theory.cardinal.continuum

/-!
# The cardinality of the reals

This file shows that the real numbers have cardinality continuum, i.e. `#ℝ = 𝔠`.

We show that `#ℝ ≤ 𝔠` by noting that every real number is determined by a Cauchy-sequence of the
form `ℕ → ℚ`, which has cardinality `𝔠`. To show that `#ℝ ≥ 𝔠` we define an injection from
`{0, 1} ^ ℕ` to `ℝ` with `f ↦ Σ n, f n * (1 / 3) ^ n`.

We conclude that all intervals with distinct endpoints have cardinality continuum.

## Main definitions

* `cardinal.cantor_function` is the function that sends `f` in `{0, 1} ^ ℕ` to `ℝ` by
  `f ↦ Σ' n, f n * (1 / 3) ^ n`

## Main statements

* `cardinal.mk_real : #ℝ = 𝔠`: the reals have cardinality continuum.
* `cardinal.not_countable_real`: the universal set of real numbers is not countable.
  We can use this same proof to show that all the other sets in this file are not countable.
* 8 lemmas of the form `mk_Ixy_real` for `x,y ∈ {i,o,c}` state that intervals on the reals
  have cardinality continuum.

## Notation

* `𝔠` : notation for `cardinal.continuum` in locale `cardinal`, defined in `set_theory.continuum`.

## Tags
continuum, cardinality, reals, cardinality of the reals
-/

open nat set
open_locale cardinal
noncomputable theory

namespace cardinal

variables {c : ℝ} {f g : ℕ → bool} {n : ℕ}

/-- The body of the sum in `cantor_function`.
`cantor_function_aux c f n = c ^ n` if `f n = tt`;
`cantor_function_aux c f n = 0` if `f n = ff`. -/
def cantor_function_aux (c : ℝ) (f : ℕ → bool) (n : ℕ) : ℝ := cond (f n) (c ^ n) 0

@[simp] lemma cantor_function_aux_tt (h : f n = tt) : cantor_function_aux c f n = c ^ n :=
by simp [cantor_function_aux, h]

@[simp] lemma cantor_function_aux_ff (h : f n = ff) : cantor_function_aux c f n = 0 :=
by simp [cantor_function_aux, h]

lemma cantor_function_aux_nonneg (h : 0 ≤ c) : 0 ≤ cantor_function_aux c f n :=
by { cases h' : f n; simp [h'], apply pow_nonneg h }

lemma cantor_function_aux_eq (h : f n = g n) :
  cantor_function_aux c f n = cantor_function_aux c g n :=
by simp [cantor_function_aux, h]

lemma cantor_function_aux_zero (f : ℕ → bool) :
  cantor_function_aux c f 0 = cond (f 0) 1 0 :=
by { cases h : f 0; simp [h] }

lemma cantor_function_aux_succ (f : ℕ → bool) :
  (λ n, cantor_function_aux c f (n + 1)) = λ n, c * cantor_function_aux c (λ n, f (n + 1)) n :=
by { ext n, cases h : f (n + 1); simp [h, pow_succ] }

lemma summable_cantor_function (f : ℕ → bool) (h1 : 0 ≤ c) (h2 : c < 1) :
  summable (cantor_function_aux c f) :=
begin
  apply (summable_geometric_of_lt_1 h1 h2).summable_of_eq_zero_or_self,
  intro n, cases h : f n; simp [h]
end

/-- `cantor_function c (f : ℕ → bool)` is `Σ n, f n * c ^ n`, where `tt` is interpreted as `1` and
`ff` is interpreted as `0`. It is implemented using `cantor_function_aux`. -/
def cantor_function (c : ℝ) (f : ℕ → bool) : ℝ := ∑' n, cantor_function_aux c f n

lemma cantor_function_le (h1 : 0 ≤ c) (h2 : c < 1) (h3 : ∀ n, f n → g n) :
  cantor_function c f ≤ cantor_function c g :=
begin
  apply tsum_le_tsum _ (summable_cantor_function f h1 h2) (summable_cantor_function g h1 h2),
  intro n, cases h : f n, simp [h, cantor_function_aux_nonneg h1],
  replace h3 : g n = tt := h3 n h, simp [h, h3]
end

lemma cantor_function_succ (f : ℕ → bool) (h1 : 0 ≤ c) (h2 : c < 1) :
  cantor_function c f = cond (f 0) 1 0 + c * cantor_function c (λ n, f (n+1)) :=
begin
  rw [cantor_function, tsum_eq_zero_add (summable_cantor_function f h1 h2)],
  rw [cantor_function_aux_succ, tsum_mul_left, cantor_function_aux, pow_zero],
  refl
end

/-- `cantor_function c` is strictly increasing with if `0 < c < 1/2`, if we endow `ℕ → bool` with a
lexicographic order. The lexicographic order doesn't exist for these infinitary products, so we
explicitly write out what it means. -/
lemma increasing_cantor_function (h1 : 0 < c) (h2 : c < 1 / 2) {n : ℕ} {f g : ℕ → bool}
  (hn : ∀(k < n), f k = g k) (fn : f n = ff) (gn : g n = tt) :
  cantor_function c f < cantor_function c g :=
begin
  have h3 : c < 1, { apply h2.trans, norm_num },
  induction n with n ih generalizing f g,
  { let f_max : ℕ → bool := λ n, nat.rec ff (λ _ _, tt) n,
    have hf_max : ∀n, f n → f_max n,
    { intros n hn, cases n, rw [fn] at hn, contradiction, apply rfl },
    let g_min : ℕ → bool := λ n, nat.rec tt (λ _ _, ff) n,
    have hg_min : ∀n, g_min n → g n,
    { intros n hn, cases n, rw [gn], apply rfl, contradiction },
    apply (cantor_function_le (le_of_lt h1) h3 hf_max).trans_lt,
    refine lt_of_lt_of_le _ (cantor_function_le (le_of_lt h1) h3 hg_min),
    have : c / (1 - c) < 1,
    { rw [div_lt_one, lt_sub_iff_add_lt],
      { convert add_lt_add h2 h2, norm_num },
      rwa sub_pos },
    convert this,
    { rw [cantor_function_succ _ (le_of_lt h1) h3, div_eq_mul_inv,
          ←tsum_geometric_of_lt_1 (le_of_lt h1) h3],
      apply zero_add },
    { refine (tsum_eq_single 0 _).trans _,
      { intros n hn, cases n, contradiction, refl },
      { exact cantor_function_aux_zero _ }, } },
  rw [cantor_function_succ f (le_of_lt h1) h3, cantor_function_succ g (le_of_lt h1) h3],
  rw [hn 0 $ zero_lt_succ n],
  apply add_lt_add_left, rw mul_lt_mul_left h1, exact ih (λ k hk, hn _ $ nat.succ_lt_succ hk) fn gn
end

/-- `cantor_function c` is injective if `0 < c < 1/2`. -/
lemma cantor_function_injective (h1 : 0 < c) (h2 : c < 1 / 2) :
  function.injective (cantor_function c) :=
begin
  intros f g hfg, classical, by_contra h, revert hfg,
  have : ∃n, f n ≠ g n,
  { rw [←not_forall], intro h', apply h, ext, apply h' },
  let n := nat.find this,
  have hn : ∀ (k : ℕ), k < n → f k = g k,
  { intros k hk, apply of_not_not, exact nat.find_min this hk },
  cases fn : f n,
  { apply ne_of_lt, refine increasing_cantor_function h1 h2 hn fn _,
    apply eq_tt_of_not_eq_ff, rw [←fn], apply ne.symm, exact nat.find_spec this },
  { apply ne_of_gt, refine increasing_cantor_function h1 h2 (λ k hk, (hn k hk).symm) _ fn,
    apply eq_ff_of_not_eq_tt, rw [←fn], apply ne.symm, exact nat.find_spec this }
end

/-- The cardinality of the reals, as a type. -/
lemma mk_real : #ℝ = 𝔠 :=
begin
  apply le_antisymm,
  { rw real.equiv_Cauchy.cardinal_eq,
    apply mk_quotient_le.trans, apply (mk_subtype_le _).trans_eq,
    rw [← power_def, mk_nat, mk_rat, aleph_0_power_aleph_0] },
  { convert mk_le_of_injective (cantor_function_injective _ _),
    rw [←power_def, mk_bool, mk_nat, two_power_aleph_0], exact 1 / 3, norm_num, norm_num }
end

/-- The cardinality of the reals, as a set. -/
lemma mk_univ_real : #(set.univ : set ℝ) = 𝔠 :=
by rw [mk_univ, mk_real]

/-- **Non-Denumerability of the Continuum**: The reals are not countable. -/
lemma not_countable_real : ¬ (set.univ : set ℝ).countable :=
by { rw [← le_aleph_0_iff_set_countable, not_le, mk_univ_real], apply cantor }

/-- The cardinality of the interval (a, ∞). -/
lemma mk_Ioi_real (a : ℝ) : #(Ioi a) = 𝔠 :=
begin
  refine le_antisymm (mk_real ▸ mk_set_le _) _,
  rw [← not_lt], intro h,
  refine ne_of_lt _ mk_univ_real,
  have hu : Iio a ∪ {a} ∪ Ioi a = set.univ,
  { convert Iic_union_Ioi, exact Iio_union_right },
  rw ← hu,
  refine lt_of_le_of_lt (mk_union_le _ _) _,
  refine lt_of_le_of_lt (add_le_add_right (mk_union_le _ _) _) _,
  have h2 : (λ x, a + a - x) '' Ioi a = Iio a,
  { convert image_const_sub_Ioi _ _, simp },
  rw ← h2,
  refine add_lt_of_lt (cantor _).le _ h,
  refine add_lt_of_lt (cantor _).le (mk_image_le.trans_lt h) _,
  rw mk_singleton,
  exact one_lt_aleph_0.trans (cantor _)
end

/-- The cardinality of the interval [a, ∞). -/
lemma mk_Ici_real (a : ℝ) : #(Ici a) = 𝔠 :=
le_antisymm (mk_real ▸ mk_set_le _) (mk_Ioi_real a ▸ mk_le_mk_of_subset Ioi_subset_Ici_self)

/-- The cardinality of the interval (-∞, a). -/
lemma mk_Iio_real (a : ℝ) : #(Iio a) = 𝔠 :=
begin
  refine le_antisymm (mk_real ▸ mk_set_le _) _,
  have h2 : (λ x, a + a - x) '' Iio a = Ioi a,
  { convert image_const_sub_Iio _ _, simp },
  exact mk_Ioi_real a ▸ h2 ▸ mk_image_le
end

/-- The cardinality of the interval (-∞, a]. -/
lemma mk_Iic_real (a : ℝ) : #(Iic a) = 𝔠 :=
le_antisymm (mk_real ▸ mk_set_le _) (mk_Iio_real a ▸ mk_le_mk_of_subset Iio_subset_Iic_self)

/-- The cardinality of the interval (a, b). -/
lemma mk_Ioo_real {a b : ℝ} (h : a < b) : #(Ioo a b) = 𝔠 :=
begin
  refine le_antisymm (mk_real ▸ mk_set_le _) _,
  have h1 : #((λ x, x - a) '' Ioo a b) ≤ #(Ioo a b) := mk_image_le,
  refine le_trans _ h1,
  rw [image_sub_const_Ioo, sub_self],
  replace h := sub_pos_of_lt h,
  have h2 : #(has_inv.inv '' Ioo 0 (b - a)) ≤ #(Ioo 0 (b - a)) := mk_image_le,
  refine le_trans _ h2,
  rw [image_inv, inv_Ioo_0_left h, mk_Ioi_real]
end

/-- The cardinality of the interval [a, b). -/

/-- The cardinality of the interval [a, b]. -/
lemma mk_Icc_real {a b : ℝ} (h : a < b) : #(Icc a b) = 𝔠 :=
le_antisymm (mk_real ▸ mk_set_le _) (mk_Ioo_real h ▸ mk_le_mk_of_subset Ioo_subset_Icc_self)

/-- The cardinality of the interval (a, b]. -/
lemma mk_Ioc_real {a b : ℝ} (h : a < b) : #(Ioc a b) = 𝔠 :=
le_antisymm (mk_real ▸ mk_set_le _) (mk_Ioo_real h ▸ mk_le_mk_of_subset Ioo_subset_Ioc_self)

end cardinal


/-
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 2 : `true` and `false`

We learn about the `true` and `false` propositions.

## Tactics you will need

To solve the levels on this sheet you will need to know all previous
tactics, plus the following two new ones. Check out their explanations
in the course book. Or just try them out and hover over them to see
if you can understand what's going on.

* `triv`
* `exfalso`

-/

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

variables (P Q R : Prop)

example : true :=
begin
  triv,
end

example : true :=
begin
  exact true.intro,
end

example : true → true :=
begin
  intro _,
  triv,
end

example : false → true :=
begin
  intro _,
  triv,
end

example : false → false :=
begin
  intro f,
  exact f,
end

example : false → P :=
begin
  intro f,
  cases f, -- there are no cases to examine, so trivially true
end

example : (true → false) → false :=
begin
  intro hTF,
  apply hTF,
  triv,
end

example : false → P :=
begin
  intro h,
  exfalso,
  exact h,
end

example : true → false → true → false → true → false :=
begin
  intros _ hf,
  exfalso,
  exact hf,
end

example : P → ((P → false) → false) :=
begin
  intros hP hPF,
  exact hPF hP,
end

example : (P → false) → P → Q :=
begin
  intros hPF hP,
  exfalso,
  exact hPF hP,
end

example : (true → false) → P :=
begin
  intro hTF,
  exfalso,
  apply hTF,
  triv,
end


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

/-!
# Complex trigonometric functions

Basic facts and derivatives for the complex trigonometric functions.
-/

noncomputable theory

namespace complex

open set filter
open_locale real

lemma has_strict_deriv_at_tan {x : ℂ} (h : cos x ≠ 0) :
  has_strict_deriv_at tan (1 / (cos x)^2) x :=
begin
  convert (has_strict_deriv_at_sin x).div (has_strict_deriv_at_cos x) h,
  rw ← sin_sq_add_cos_sq x,
  ring,
end

lemma has_deriv_at_tan {x : ℂ} (h : cos x ≠ 0) :
  has_deriv_at tan (1 / (cos x)^2) x :=
(has_strict_deriv_at_tan h).has_deriv_at

open_locale topological_space

lemma tendsto_abs_tan_of_cos_eq_zero {x : ℂ} (hx : cos x = 0) :
  tendsto (λ x, abs (tan x)) (𝓝[≠] x) at_top :=
begin
  simp only [tan_eq_sin_div_cos, ← norm_eq_abs, norm_div],
  have A : sin x ≠ 0 := λ h, by simpa [*, sq] using sin_sq_add_cos_sq x,
  have B : tendsto cos (𝓝[≠] (x)) (𝓝[≠] 0),
    from hx ▸ (has_deriv_at_cos x).tendsto_punctured_nhds (neg_ne_zero.2 A),
  exact continuous_sin.continuous_within_at.norm.mul_at_top (norm_pos_iff.2 A)
    (tendsto_norm_nhds_within_zero.comp B).inv_tendsto_zero,
end

lemma tendsto_abs_tan_at_top (k : ℤ) :
  tendsto (λ x, abs (tan x)) (𝓝[≠] ((2 * k + 1) * π / 2)) at_top :=
tendsto_abs_tan_of_cos_eq_zero $ cos_eq_zero_iff.2 ⟨k, rfl⟩

@[simp] lemma continuous_at_tan {x : ℂ} : continuous_at tan x ↔ cos x ≠ 0 :=
begin
  refine ⟨λ hc h₀, _, λ h, (has_deriv_at_tan h).continuous_at⟩,
  exact not_tendsto_nhds_of_tendsto_at_top (tendsto_abs_tan_of_cos_eq_zero h₀) _
    (hc.norm.tendsto.mono_left inf_le_left)
end

@[simp] lemma differentiable_at_tan {x : ℂ} : differentiable_at ℂ tan x ↔ cos x ≠ 0 :=
⟨λ h, continuous_at_tan.1 h.continuous_at, λ h, (has_deriv_at_tan h).differentiable_at⟩

@[simp] lemma deriv_tan (x : ℂ) : deriv tan x = 1 / (cos x)^2 :=
if h : cos x = 0 then
  have ¬differentiable_at ℂ tan x := mt differentiable_at_tan.1 (not_not.2 h),
  by simp [deriv_zero_of_not_differentiable_at this, h, sq]
else (has_deriv_at_tan h).deriv

@[simp] lemma cont_diff_at_tan {x : ℂ} {n : with_top ℕ} :
  cont_diff_at ℂ n tan x ↔ cos x ≠ 0 :=
⟨λ h, continuous_at_tan.1 h.continuous_at,
  cont_diff_sin.cont_diff_at.div cont_diff_cos.cont_diff_at⟩

end complex


-- Reglas de la intersección general
-- =================================

import data.set
open set

section
variables {I U : Type}
variables {A : I → set U}
variable  {x : U}

-- Regla de introducción de la intersección
-- ========================================

-- 1ª demostración
example
  (h : ∀ i, x ∈ A i)
  : x ∈ ⋂ i, A i :=
begin
  simp,
  assumption,
end

-- 2ª demostración
theorem Inter.intro
  (h : ∀ i, x ∈ A i)
  : x ∈ ⋂ i, A i :=
by simp; assumption

-- Regla de eliminación de la intersección
-- =======================================

-- 1ª demostración
example
  (h : x ∈ ⋂ i, A i)
  (i : I)
  : x ∈ A i :=
begin
  simp at h,
  apply h,
end

-- 2ª demostración
@[elab_simple]
theorem Inter.elim
  (h : x ∈ ⋂ i, A i)
  (i : I)
  : x ∈ A i :=
by simp at h; apply h

end


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

import .basic

namespace fin

lemma forall_iff_zero_and_succ {n} (p : fin (n+1) → Prop) :
p 0 ∧ (∀ i, p (fin.succ i)) ↔ (∀ i, p i) :=
iff.intro
  (λ ⟨ho,hs⟩ i,
  match i with
  | ⟨0,_⟩ := ho
  | ⟨i+1,hi⟩ := hs ⟨i, nat.lt_of_succ_lt_succ hi⟩
  end)
  (λ h, ⟨h 0, λ i, h (fin.succ i)⟩)

lemma forall_iff_lift_and_last {n} (p : fin (n+1) → Prop) :
(∀ i, p (fin.lift i)) ∧ p (fin.last n) ↔ (∀ i, p i) :=
iff.intro
  (λ ⟨hl,h⟩ i,
  if hi : i = fin.last n then
  eq.substr hi h
  else
  have fin.lift (fin.drop i hi) = i, from lift_drop _ _,
  eq.subst this $ hl (fin.drop i hi))
  (λ h, ⟨λ i, h (fin.lift i), h (fin.last n)⟩)

lemma exists_iff_zero_or_succ {n} (p : fin (n+1) → Prop) :
p 0 ∨ (∃ i, p (fin.succ i)) ↔ (∃ i, p i) :=
iff.intro
  (λ h, or.elim h (λ ho, ⟨0, ho⟩) (λ ⟨i,hi⟩, ⟨fin.succ i, hi⟩))
  (λ ⟨i,hi⟩,
  match i,hi with
  | ⟨0,_⟩,ho := or.inl ho
  | ⟨i+1,hi⟩,hs := or.inr (exists.intro ⟨i, nat.lt_of_succ_lt_succ hi⟩ hs)
  end)

lemma exists_iff_lift_or_last {n} (p : fin (n+1) → Prop) :
(∃ i, p (fin.lift i)) ∨ p (fin.last n) ↔ (∃ i, p i) :=
iff.intro
  (λ h, or.elim h (λ ⟨i,h⟩, ⟨fin.lift i, h⟩) (λ h, ⟨fin.last n, h⟩))
  (λ ⟨i,h⟩,
  if hi : i = fin.last n then
  or.inr (eq.subst hi h)
  else
  or.inl
    (have p (fin.lift (fin.drop i hi)),
    from eq.substr (lift_drop i hi) h,
    ⟨fin.drop i hi, this⟩))

instance forall_decidable :
Π {n : ℕ} (p : fin n → Prop) [decidable_pred p], decidable (∀ i, p i)
| 0 p _ := decidable.is_true (λ (i : fin 0), fin.elim0 i)
| (n+1) p dp :=
  have d0: decidable (p (fin.zero n)), from dp (fin.zero n),
  have ds: decidable (∀ i, p (fin.succ i)),
  from @forall_decidable n (λ i, p (fin.succ i)) (λ i, dp (fin.succ i)),
  decidable_of_decidable_of_iff (@and.decidable _ _ d0 ds) (forall_iff_zero_and_succ p)

instance exists_decidable :
Π {n : ℕ} (p : fin n → Prop) [decidable_pred p], decidable (∃ i, p i)
| 0 p _ := decidable.is_false (λ ⟨i,_⟩, fin.elim0 i)
| (n+1) p dp :=
  have d0: decidable (p (fin.zero n)), from dp (fin.zero n),
  have ds: decidable (∃ i, p (fin.succ i)),
  from @exists_decidable n (λ i, p (fin.succ i)) (λ i, dp (fin.succ i)),
  decidable_of_decidable_of_iff (@or.decidable _ _ d0 ds) (exists_iff_zero_or_succ p)

end fin


import algebra.group
import chris_hughes_various.zmod
import group_theory.order_of_element

open zmod nat

-- I spell this one out so you can see how it goes.
lemma gcd_one_of_unit {n : ℕ} [pos_nat n] (u : units (zmod n)) :
nat.gcd (u.val.val) n = 1 :=
begin
  let abar := u.val, let bbar := u.inv, --  in zmod n
  let a := abar.val, let b := bbar.val, -- in ℕ
  have H : (a * b) % n = 1 % n,
    show (abar.val * bbar.val) % n = 1 % n,
    rw ←mul_val,
    rw u.val_inv,
    refl,
  let d := nat.gcd a n,
  show d = 1,
  rw ←nat.dvd_one,
  rw ←dvd_mod_iff (gcd_dvd_right a n),
  rw ←H,
  rw dvd_mod_iff (gcd_dvd_right a n),
  apply dvd_mul_of_dvd_left,
  exact gcd_dvd_left a n
end

-- this one comes for free now, and it's the one we want
lemma gcd_one_of_has_inv {n : ℕ} [pos_nat n] (a : zmod n) (Hinv : ∃ b, a * b = 1) :
nat.gcd (a.val) n = 1 :=
begin
  cases Hinv with b Hb,
  let u : units (zmod n) := ⟨a,b,Hb,mul_comm a b ▸ Hb⟩,
  exact gcd_one_of_unit u
end

-- thanks Chris :-)
@[simp] lemma cast_val {n : ℕ} [pos_nat n] (a : zmod n) : (a.val : zmod n) = a :=
by cases a; simp [mk_eq_cast]

def coprime_zmodn_units (n : ℕ) [pos_nat n] :
equiv (units (zmod n)) {a : zmod n // ∃ b, a * b = 1} :=
{ to_fun := λ u, ⟨u.1, u.2, u.3⟩,
  inv_fun := λ A,
  { val := (A.val).val, inv := ((A.val).val⁻¹),
    val_inv := by rw [mul_inv_eq_gcd,gcd_one_of_has_inv A.val A.property];dsimp;rw zero_add,
    inv_val := by rw [mul_comm,mul_inv_eq_gcd,gcd_one_of_has_inv A.val A.property];dsimp;rw zero_add,
  },
  left_inv := λ u,begin apply units.ext,show (↑((u.val).val) : zmod n) = u.val,simp,end,
  right_inv := λ A, by simp,
}

instance (n : nat) : pos_nat (nat.succ n) := ⟨nat.succ_pos _⟩
instance (n : ℕ) [pos_nat n] : fintype (units (zmod n)) := fintype.of_equiv _ (equiv.symm (coprime_zmodn_units n))
instance decidable_eq_units_zmod (n : ℕ) [pos_nat n] [monoid (zmod n)] : decidable_eq (units (zmod n)) :=  λ x y, decidable_of_iff _ ⟨ units.ext, λ _,by simp *⟩

#eval @order_of (units (zmod 7)) _ _ _ ⟨(2 : zmod 7), 2⁻¹, rfl, rfl⟩
#eval @order_of (units (zmod 5)) _ _ _ ⟨(2 : zmod 5), 2⁻¹, rfl, rfl⟩
#eval @order_of (units (zmod 7)) _ _ _ ⟨(1 : zmod 7), 1⁻¹, rfl, rfl⟩


inductive xnat
| zero : xnat
| succ : xnat → xnat
open xnat
#print xnat.succ.inj
#print xnat.no_confusion
definition one := succ zero
definition two := succ one
definition add :xnat → xnat → xnat
| n zero := n
| n (succ p) := succ (add n p)
notation a + b := add a b
theorem one_add_one_equals_two : one + one = two :=
    begin
        unfold two,
        unfold one,
        unfold add,
    end
theorem add_zerox (n:xnat): n+zero=n:=
    begin
        unfold add,
    end
theorem zero_addx (n:xnat):zero+n=n:=
    begin
        induction n with k H,
        unfold add,
        unfold add,
        rw[H],
    end
theorem add_assocx (a b c:xnat):(a+b)+c=a+(b+c):=
    begin
        induction c with k H,
        unfold add,
        unfold add,
        rw[H],
    end
theorem zero_add_eq_add_zerox (n:xnat) : zero+n=n+zero:=
    begin
        rw[zero_addx,add_zerox],
    end
theorem add_one_eq_succx (n:xnat) : n + one = succ n:=
    begin
        unfold one add,
    end
theorem one_add_eq_succx (n : xnat) : one+n=succ n:=
    begin
        induction n with k H,
        unfold one add,
        unfold one add,
        rw[←H],
        unfold one,
    end
theorem succ_addx (a b:xnat) : succ (a + b) = succ a + b:=begin
    induction b with b hi,
    trivial,
    unfold add,rw hi,
end
theorem add_commx (a b:xnat) : a+b = b+a:=
    begin
        induction b with k H,
        rw[zero_add_eq_add_zerox],
        unfold add,
        rw [H,succ_addx],
    end
theorem eq_iff_succ_eq_succ (a b : xnat) : succ a = succ b ↔ a = b :=
    begin
        split,
        exact succ.inj,
        assume H : a = b,
        rw [H],
    end
theorem add_cancel_right (a b t : xnat) :  a = b ↔ a+t = b+t :=
    begin
        split,
        assume H,
        rw[H],
        induction t with k H,
        rw[add_zerox,add_zerox],
        assume H1,
        exact H1,
        unfold add,
        rw[eq_iff_succ_eq_succ],
        exact H,
    end
definition mul:xnat→xnat→xnat
| n zero:=zero
| n (succ p):= mul n p + n
notation a * b := mul a b
theorem mul_zerox (a : xnat) : a * zero = zero :=
    begin
        trivial,
    end
theorem zero_mulx (a : xnat) : zero * a = zero :=
    begin
        induction a with k H,
        unfold mul,
        unfold mul add,
        rw[H],
    end
theorem mul_onex (a : xnat) : a * one = a :=
    begin
        unfold one mul,
        rw[zero_addx],
    end
theorem one_mulx (a : xnat) : one * a = a :=
    begin
        induction a with k H,
        unfold mul,
        unfold mul,
        rw[add_one_eq_succx, H],
    end
theorem right_distribx (a b c : xnat) : a * (b + c) = a* b + a * c :=
    begin
        induction c with k H,
        rw[mul_zerox,add_zerox,add_zerox],
        unfold add mul,
        rw[H, add_assocx],
    end
theorem left_distribx (a b c : xnat) : (a + b) * c = a * c + b * c :=
    begin
        induction c with n Hn,
        unfold mul,
        refl,
        rw [←add_one_eq_succx,right_distribx,Hn,right_distribx,right_distribx],
        rw [mul_onex,mul_onex,mul_onex],
        rw [add_assocx,←add_assocx (b*n),add_commx (b*n),←add_assocx,←add_assocx,←add_assocx],
    end
theorem mul_assocx (a b c : xnat) : (a * b) * c = a * (b * c) :=
    begin
        induction c with k H,
        rw[mul_zerox,mul_zerox,mul_zerox],
        unfold mul,
        rw[right_distribx,H]
    end
theorem mul_commx (a b : xnat) : a * b = b * a :=
    begin
        induction b with k H,
        rw[mul_zerox,zero_mulx],
        unfold mul,
        rw[H],
        exact calc k * a + a = k * a + one * a: by rw[one_mulx]
        ...=(k + one) * a: by rw[left_distribx]
        ...=succ k * a: by rw[add_one_eq_succx],
    end
definition lt : xnat → xnat → Prop
    | zero zero := false
    | (succ m) zero := false
    | zero (succ p) := true
    | (succ m) (succ p) := lt m p

notation b > a := lt a b
notation a < b := lt a b

theorem inequality_A1 (a b t : xnat) : a < b → a + t < b + t :=
    begin
        induction t with n H,
        rw[add_zerox,add_zerox],
        assume H1,
        exact H1,
        unfold add lt, exact H,
    end
theorem blah: ∀a b c:xnat,a<b→b<c→a<c:=begin
    assume a,
    induction a with a1 Hia,
        assume b c,
        cases b with b1,
            unfold lt,cc,

            cases c with c1,
                unfold lt,cc,

                unfold lt,cc,
        assume b c,
        cases b with b1,
            unfold lt,cc,

            cases c with c1,
                unfold lt,cc,

                unfold lt,
                exact Hia b1 c1,
end
theorem blah1: ∀a b c:xnat,a<b→b<c→a<c:=begin
    assume a b, revert a,
    induction b with b1 Hib,
        assume a,
        cases a with a1,
            unfold lt,cc,

            unfold lt,cc,
        assume a c,
        cases c with c1,
            unfold lt,cc,

        cases a with a1,
            unfold lt,cc,

            unfold lt,exact Hib a1 c1,
end
theorem blah2: ∀a b c:xnat,a<b→b<c→a<c:=begin
    assume a b c,revert a b,
    induction c with c1 Hic,
        assume a b,
        cases b with b1,
            unfold lt,cc,

            unfold lt,cc,

        assume a b,
        cases a with a1,
            unfold lt,cc,

            cases b with b1,
                unfold lt,trivial,

                unfold lt,exact Hic a1 b1,
end
#check list.
#print blah2
theorem subtraction :∀ a b:xnat,a<b→∃c,c+a=b:=begin
    assume a,
    induction a with a1 Hia,
        assume b H1,
        existsi b, unfold add,

        assume b1,
        cases b1 with b2,
            unfold lt,trivial,

            unfold lt,
            assume H2,
            apply exists.elim (Hia b2 H2),
            assume c H3,
            existsi c,rw ←H3,unfold add,

end
theorem a_lt_a_add_succ_b (a b:xnat):a<a+succ b:=begin
    induction a with a1 Ha,
    rw zero_addx,unfold lt,
    unfold add lt,rwa[←add_one_eq_succx,add_assocx,one_add_eq_succx],
end
theorem blah3 (x y:xnat):zero<x→one<y→x<x*y:=begin
    cases x with x1,
        unfold lt,cc,

        cases y with y1,
            unfold one lt,cc,

            cases y1 with y2,
                unfold one lt,cc,

                unfold one lt mul,
                rw[add_commx,←one_add_eq_succx,add_assocx,one_add_eq_succx,one_add_eq_succx],
                unfold lt,
                have H1:x1 + (succ x1 * y2 + succ x1)=x1+succ (succ x1 * y2 + x1):=calc
                    x1 + (succ x1 * y2 + succ x1) = x1 + (succ x1 * y2 + (x1+one)):begin rw ←add_one_eq_succx end
                    ...=x1 + (succ x1 * y2 + x1+one):begin rw add_assocx, end
                    ...=x1 +succ (succ x1 * y2 + x1):begin rw add_one_eq_succx, end,
                rw H1,
                assume H2 H3,
                exact a_lt_a_add_succ_b x1 (succ x1 * y2 + x1),
end


/-
Copyright (c) 2023 María Inés de Frutos-Fernández. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : María Inés de Frutos-Fernández
-/

import tactic
import data.nat.parity
import data.real.basic
import data.finset.basic
import data.real.basic

/-!
# Nuevas tácticas
* `refine`
* `obtain`
* `swap`
* `library_search`
* `simp`
* `group`
-/

/- ## refine
Funciona como exact, pero permitiendo utilizar barras bajas _ en la expresión; cada una de ellas
será reemplazada por la submeta correspondiente.

Por ejemplo, dada una meta de forma `P ∧ Q`, `refine ⟨_, _⟩`, es equivalente a `split`. Pero
`refine` es más flexible: si por ejemplo ya tenemos una demostración `hP : P`, podemos usar
`refine ⟨hP, _⟩` y la meta pasará a ser `⊢ Q`.
-/

example {n : ℕ} : even n ↔ even (n^2) :=
begin
  refine ⟨λ hn, even.pow_of_ne_zero hn (two_ne_zero), λ hn, _⟩,
  rw [pow_two, nat.even_mul, or_self] at hn,
  exact hn,
end
/- ## obtain
La táctica `obtain` combina `have` y `cases`.
La instrucción `obtain ⟨pattern⟩ : type := proof` es equivalente a
```
have h : type,
{ ... },
rcases h with ⟨patt⟩
```
En muchos casos no es necesario incluir el tipo explícitamente
-/

example {n : ℕ} (hn : even n) : odd (n + 1) :=
begin
  obtain ⟨k, hk⟩ := hn,
  refine ⟨k, _⟩,
  rw [hk, two_mul],
end

example {X Y Z : Type*} {f : X → Y} {g : Y → Z} (hf : function.surjective f)
  (hg : function.surjective g) : function.surjective (g ∘ f) :=
begin
  intros z,
  have hgz : ∃ (a : Y), g a = z,
  { exact hg z},
  cases hgz with y hy,
  obtain ⟨x, hx⟩ := hf y,
  use x,
  rw [function.comp_app, hx, hy],
end

/- ## swap
La táctica `swap` intercambia las dos primeras metas.

También se puede utilizar como `swap n`, que convierte la enésima meta en la primera.
-/

example {Ω : Type*} {X Y : set Ω} (hXY : X ⊆ Y) (hYX : Y ⊆ X) : X = Y :=
begin
  ext a,
  split,
  swap,
  { apply hYX },
  { apply hXY },
end

/- ## library_search
`library_search` intenta cerrar la meta actual aplicando un lema existente en la librería mathlib.
Si lo encuentra, imprime un mensaje `exact ...` con la solución empleada.

-/

--example {n m : ℕ} : n + m = m + n := by library_search

--example {n m : ℕ} (hn : even n) (hm : odd m) : odd (m + n) := by library_search

/- ## simp
Muchos lemas de mathlib que demuestran igualdades o equivalencias lógicas están marcados con la
etiqueta `@[simp]`. También podemos utilizar esta etiqueta en lemas que demostremos.

La táctica `simp` intenta encontrar lemas cuyo lado izquierdo aparece en la meta, y sustituirlos por
el lado derecho (por tanto, si creamos un lema marcado `@[simp]`, el lado derecho debe ser la
expresión más sencilla).

También podemos usar `simp [lema1, lema2, ...]` para que, además de los lemas etiquetados, el
simplificador también intente utilizar los lemas proporcionados.

Podemos utilizar `simp` en hipótesis locales, con la sintaxis `simp at h`.

No es buena práctica utilizar `simp` en el medio de una demostración; es mejor sustituirlo por
una aplicación de `simp only [...]`, como veremos abajo.
-/

example : (0 : ℝ) + 1 = 1 + 0 := by simp

open_locale big_operators
open finset

example (n : ℕ) : ∑ i in range n, (i : ℝ) = n * (n - 1) / 2 :=
begin
  induction n with d hd,
  { -- la suma sobre el conjunto vacío es 0 * (0 - 1) / 2
    simp },
  { -- inducción
    rw [sum_range_succ, hd],
    simp, -- reduce la meta a ⊢ ↑d * (↑d - 1) / 2 + ↑d = (↑d + 1) * ↑d / 2
    ring,
  }
end

/- ### simp only
Si utilizamos `simp only [h₁ h₂ ... hₙ]` en lugar de `simp [h₁ h₂ ... hₙ]`, el simplificador sólo
utilizará los lemas hᵢ, pero no los lemas marcados con `@[simp]`.
-/

example (n : ℕ) : ∑ i in range n, (i : ℝ) = n * (n - 1) / 2 :=
begin
  induction n with d hd,
  { -- la suma sobre el conjunto vacío es 0 * (0 - 1) / 2
    simp only [range_zero, sum_empty, nat.cast_zero, zero_mul, zero_div], },
  { -- inducción
    rw [sum_range_succ, hd],
    simp only [nat.cast_succ, add_tsub_cancel_right],
    -- reduce la meta a ⊢ ↑d * (↑d - 1) / 2 + ↑d = (↑d + 1) * ↑d / 2
    ring,
  }
end

/- ### squeeze_simp
`squeeze_simp` funciona como `simp`, y además imprime un mensaje `simp only [...]` con la lista
de lemas que han sido utilizados.
-/

--example : (0 : ℝ) + 1 = 1 + 0 := by squeeze_simp

/- ## group
Esta táctica se utiliza para simplificar expresiones en grupos multiplicativos, utilizando sólo
los axiomas de grupo, sin asumir conmutatividad.

No utiliza las hipótesis locales, por lo que es probable que haya que combinarla con otras tácticas
como rw.
-/

-- Ejemplo tomado de la documentación de mathlib
example {G : Type} [group G] (a b c d : G) (h : c = (a*b^2)*((b*b)⁻¹*a⁻¹)*d) : a*c*d⁻¹ = a :=
begin
  group at h, -- normaliza `h`, obteniendo `h : c = d`
  rw h,       -- la meta es ahora `a*d*d⁻¹ = a`
  group,      -- group cierra la meta
end


/-
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import data.list.big_operators.basic
import data.multiset.basic

/-!
# Sums and products over multisets

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

In this file we define products and sums indexed by multisets. This is later used to define products
and sums indexed by finite sets.

## Main declarations

* `multiset.prod`: `s.prod f` is the product of `f i` over all `i ∈ s`. Not to be mistaken with
  the cartesian product `multiset.product`.
* `multiset.sum`: `s.sum f` is the sum of `f i` over all `i ∈ s`.

## Implementation notes

Nov 2022: To speed the Lean 4 port, lemmas requiring extra algebra imports
(`data.list.big_operators.lemmas` rather than `.basic`) have been moved to a separate file,
`algebra.big_operators.multiset.lemmas`.  This split does not need to be permanent.
-/

variables {ι α β γ : Type*}

namespace multiset
section comm_monoid
variables [comm_monoid α] {s t : multiset α} {a : α} {m : multiset ι} {f g : ι → α}

/-- Product of a multiset given a commutative monoid structure on `α`.
  `prod {a, b, c} = a * b * c` -/
@[to_additive "Sum of a multiset given a commutative additive monoid structure on `α`.
  `sum {a, b, c} = a + b + c`"]
def prod : multiset α → α := foldr (*) (λ x y z, by simp [mul_left_comm]) 1

@[to_additive]
lemma prod_eq_foldr (s : multiset α) : prod s = foldr (*) (λ x y z, by simp [mul_left_comm]) 1 s :=
rfl

@[to_additive]
lemma prod_eq_foldl (s : multiset α) : prod s = foldl (*) (λ x y z, by simp [mul_right_comm]) 1 s :=
(foldr_swap _ _ _ _).trans (by simp [mul_comm])

@[simp, norm_cast, to_additive] lemma coe_prod (l : list α) : prod ↑l = l.prod := prod_eq_foldl _

@[simp, to_additive]
lemma prod_to_list (s : multiset α) : s.to_list.prod = s.prod :=
begin
  conv_rhs { rw ←coe_to_list s },
  rw coe_prod,
end

@[simp, to_additive] lemma prod_zero : @prod α _ 0 = 1 := rfl

@[simp, to_additive]
lemma prod_cons (a : α) (s) : prod (a ::ₘ s) = a * prod s := foldr_cons _ _ _ _ _

@[simp, to_additive]
lemma prod_erase [decidable_eq α] (h : a ∈ s) : a * (s.erase a).prod = s.prod :=
by rw [← s.coe_to_list, coe_erase, coe_prod, coe_prod, list.prod_erase (mem_to_list.2 h)]

@[simp, to_additive]
lemma prod_map_erase [decidable_eq ι] {a : ι} (h : a ∈ m) :
  f a * ((m.erase a).map f).prod = (m.map f).prod :=
by rw [← m.coe_to_list, coe_erase, coe_map, coe_map, coe_prod, coe_prod,
  list.prod_map_erase f (mem_to_list.2 h)]

@[simp, to_additive]
lemma prod_singleton (a : α) : prod {a} = a :=
by simp only [mul_one, prod_cons, ←cons_zero, eq_self_iff_true, prod_zero]

@[to_additive]
lemma prod_pair (a b : α) : ({a, b} : multiset α).prod = a * b :=
by rw [insert_eq_cons, prod_cons, prod_singleton]

@[simp, to_additive]
lemma prod_add (s t : multiset α) : prod (s + t) = prod s * prod t :=
quotient.induction_on₂ s t $ λ l₁ l₂, by simp

lemma prod_nsmul (m : multiset α) : ∀ (n : ℕ), (n • m).prod = m.prod ^ n
| 0       := by { rw [zero_nsmul, pow_zero], refl }
| (n + 1) :=
  by rw [add_nsmul, one_nsmul, pow_add, pow_one, prod_add, prod_nsmul n]

@[simp, to_additive] lemma prod_replicate (n : ℕ) (a : α) : (replicate n a).prod = a ^ n :=
by simp [replicate, list.prod_replicate]

@[to_additive]
lemma prod_map_eq_pow_single [decidable_eq ι] (i : ι) (hf : ∀ i' ≠ i, i' ∈ m → f i' = 1) :
  (m.map f).prod = f i ^ m.count i :=
begin
  induction m using quotient.induction_on with l,
  simp [list.prod_map_eq_pow_single i f hf],
end

@[to_additive]
lemma prod_eq_pow_single [decidable_eq α] (a : α) (h : ∀ a' ≠ a, a' ∈ s → a' = 1) :
  s.prod = a ^ (s.count a) :=
begin
  induction s using quotient.induction_on with l,
  simp [list.prod_eq_pow_single a h],
end

@[to_additive]
lemma pow_count [decidable_eq α] (a : α) : a ^ s.count a = (s.filter (eq a)).prod :=
by rw [filter_eq, prod_replicate]

@[to_additive]
lemma prod_hom [comm_monoid β] (s : multiset α) {F : Type*} [monoid_hom_class F α β] (f : F) :
  (s.map f).prod = f s.prod :=
quotient.induction_on s $ λ l, by simp only [l.prod_hom f, quot_mk_to_coe, coe_map, coe_prod]

@[to_additive]
lemma prod_hom' [comm_monoid β] (s : multiset ι) {F : Type*} [monoid_hom_class F α β] (f : F)
  (g : ι → α) : (s.map $ λ i, f $ g i).prod = f (s.map g).prod :=
by { convert (s.map g).prod_hom f, exact (map_map _ _ _).symm }

@[to_additive]
lemma prod_hom₂ [comm_monoid β] [comm_monoid γ] (s : multiset ι) (f : α → β → γ)
  (hf : ∀ a b c d, f (a * b) (c * d) = f a c * f b d) (hf' : f 1 1 = 1) (f₁ : ι → α) (f₂ : ι → β) :
  (s.map $ λ i, f (f₁ i) (f₂ i)).prod = f (s.map f₁).prod (s.map f₂).prod :=
quotient.induction_on s $ λ l,
  by simp only [l.prod_hom₂ f hf hf', quot_mk_to_coe, coe_map, coe_prod]

@[to_additive]
lemma prod_hom_rel [comm_monoid β] (s : multiset ι) {r : α → β → Prop} {f : ι → α} {g : ι → β}
  (h₁ : r 1 1) (h₂ : ∀ ⦃a b c⦄, r b c → r (f a * b) (g a * c)) :
  r (s.map f).prod (s.map g).prod :=
quotient.induction_on s $ λ l,
  by simp only [l.prod_hom_rel h₁ h₂, quot_mk_to_coe, coe_map, coe_prod]

@[to_additive]
lemma prod_map_one : prod (m.map (λ i, (1 : α))) = 1 := by rw [map_const, prod_replicate, one_pow]

@[simp, to_additive]
lemma prod_map_mul : (m.map $ λ i, f i * g i).prod = (m.map f).prod * (m.map g).prod :=
m.prod_hom₂ (*) mul_mul_mul_comm (mul_one _) _ _

@[simp]
lemma prod_map_neg [has_distrib_neg α] (s : multiset α) :
  (s.map has_neg.neg).prod = (-1) ^ s.card * s.prod :=
by { refine quotient.ind _ s, simp }

@[to_additive]
lemma prod_map_pow {n : ℕ} : (m.map $ λ i, f i ^ n).prod = (m.map f).prod ^ n :=
m.prod_hom' (pow_monoid_hom n : α →* α) f

@[to_additive]
lemma prod_map_prod_map (m : multiset β) (n : multiset γ) {f : β → γ → α} :
  prod (m.map $ λ a, prod $ n.map $ λ b, f a b) = prod (n.map $ λ b, prod $ m.map $ λ a, f a b) :=
multiset.induction_on m (by simp) (λ a m ih, by simp [ih])

@[to_additive]
lemma prod_induction (p : α → Prop) (s : multiset α) (p_mul : ∀ a b, p a → p b → p (a * b))
  (p_one : p 1) (p_s : ∀ a ∈ s, p a) :
  p s.prod :=
begin
  rw prod_eq_foldr,
  exact foldr_induction (*) (λ x y z, by simp [mul_left_comm]) 1 p s p_mul p_one p_s,
end

@[to_additive]
lemma prod_induction_nonempty (p : α → Prop) (p_mul : ∀ a b, p a → p b → p (a * b))
  (hs : s ≠ ∅) (p_s : ∀ a ∈ s, p a) :
  p s.prod :=
begin
  revert s,
  refine multiset.induction _ _,
  { intro h,
    exfalso,
    simpa using h },
  intros a s hs hsa hpsa,
  rw prod_cons,
  by_cases hs_empty : s = ∅,
  { simp [hs_empty, hpsa a] },
  have hps : ∀ x, x ∈ s → p x, from λ x hxs, hpsa x (mem_cons_of_mem hxs),
  exact p_mul a s.prod (hpsa a (mem_cons_self a s)) (hs hs_empty hps),
end

lemma prod_dvd_prod_of_le (h : s ≤ t) : s.prod ∣ t.prod :=
by { obtain ⟨z, rfl⟩ := exists_add_of_le h, simp only [prod_add, dvd_mul_right] }

end comm_monoid

lemma prod_dvd_prod_of_dvd [comm_monoid β] {S : multiset α} (g1 g2 : α → β)
  (h : ∀ a ∈ S, g1 a ∣ g2 a) :
  (multiset.map g1 S).prod ∣ (multiset.map g2 S).prod :=
begin
  apply multiset.induction_on' S, { simp },
  intros a T haS _ IH,
  simp [mul_dvd_mul (h a haS) IH]
end

section add_comm_monoid
variables [add_comm_monoid α]

/-- `multiset.sum`, the sum of the elements of a multiset, promoted to a morphism of
`add_comm_monoid`s. -/
def sum_add_monoid_hom : multiset α →+ α :=
{ to_fun := sum,
  map_zero' := sum_zero,
  map_add' := sum_add }

@[simp] lemma coe_sum_add_monoid_hom : (sum_add_monoid_hom : multiset α → α) = sum := rfl

end add_comm_monoid

section comm_monoid_with_zero
variables [comm_monoid_with_zero α]

lemma prod_eq_zero {s : multiset α} (h : (0 : α) ∈ s) : s.prod = 0 :=
begin
  rcases multiset.exists_cons_of_mem h with ⟨s', hs'⟩,
  simp [hs', multiset.prod_cons]
end

variables [no_zero_divisors α] [nontrivial α] {s : multiset α}

lemma prod_eq_zero_iff : s.prod = 0 ↔ (0 : α) ∈ s :=
quotient.induction_on s $ λ l, by { rw [quot_mk_to_coe, coe_prod], exact list.prod_eq_zero_iff }

lemma prod_ne_zero (h : (0 : α) ∉ s) : s.prod ≠ 0 := mt prod_eq_zero_iff.1 h

end comm_monoid_with_zero

section division_comm_monoid
variables [division_comm_monoid α] {m : multiset ι} {f g : ι → α}

@[to_additive] lemma prod_map_inv' (m : multiset α) : (m.map has_inv.inv).prod = m.prod⁻¹ :=
m.prod_hom (inv_monoid_hom : α →* α)

@[simp, to_additive] lemma prod_map_inv : (m.map $ λ i, (f i)⁻¹).prod = (m.map f).prod ⁻¹ :=
by { convert (m.map f).prod_map_inv', rw map_map }

@[simp, to_additive]
lemma prod_map_div : (m.map $ λ i, f i / g i).prod = (m.map f).prod / (m.map g).prod :=
m.prod_hom₂ (/) mul_div_mul_comm (div_one _) _ _

@[to_additive]
lemma prod_map_zpow {n : ℤ} : (m.map $ λ i, f i ^ n).prod = (m.map f).prod ^ n :=
by { convert (m.map f).prod_hom (zpow_group_hom _ : α →* α), rw map_map, refl }

end division_comm_monoid

section non_unital_non_assoc_semiring
variables [non_unital_non_assoc_semiring α] {a : α} {s : multiset ι} {f : ι → α}

lemma sum_map_mul_left : sum (s.map (λ i, a * f i)) = a * sum (s.map f) :=
multiset.induction_on s (by simp) (λ i s ih, by simp [ih, mul_add])

lemma sum_map_mul_right : sum (s.map (λ i, f i * a)) = sum (s.map f) * a :=
multiset.induction_on s (by simp) (λ a s ih, by simp [ih, add_mul])

end non_unital_non_assoc_semiring

section semiring
variables [semiring α]

lemma dvd_sum {a : α} {s : multiset α} : (∀ x ∈ s, a ∣ x) → a ∣ s.sum :=
multiset.induction_on s (λ _, dvd_zero _)
  (λ x s ih h, by { rw sum_cons, exact dvd_add
    (h _ (mem_cons_self _ _)) (ih $ λ y hy, h _ $ mem_cons.2 $ or.inr hy) })

end semiring

/-! ### Order -/

section ordered_comm_monoid
variables [ordered_comm_monoid α] {s t : multiset α} {a : α}

@[to_additive sum_nonneg]
lemma one_le_prod_of_one_le : (∀ x ∈ s, (1 : α) ≤ x) → 1 ≤ s.prod :=
quotient.induction_on s $ λ l hl, by simpa using list.one_le_prod_of_one_le hl

@[to_additive]
lemma single_le_prod : (∀ x ∈ s, (1 : α) ≤ x) → ∀ x ∈ s, x ≤ s.prod :=
quotient.induction_on s $ λ l hl x hx, by simpa using list.single_le_prod hl x hx

@[to_additive sum_le_card_nsmul]
lemma prod_le_pow_card (s : multiset α) (n : α) (h : ∀ x ∈ s, x ≤ n) : s.prod ≤ n ^ s.card :=
begin
  induction s using quotient.induction_on,
  simpa using list.prod_le_pow_card _ _ h,
end

@[to_additive all_zero_of_le_zero_le_of_sum_eq_zero]
lemma all_one_of_le_one_le_of_prod_eq_one :
  (∀ x ∈ s, (1 : α) ≤ x) → s.prod = 1 → ∀ x ∈ s, x = (1 : α) :=
begin
  apply quotient.induction_on s,
  simp only [quot_mk_to_coe, coe_prod, mem_coe],
  exact λ l, list.all_one_of_le_one_le_of_prod_eq_one,
end

@[to_additive]
lemma prod_le_prod_of_rel_le (h : s.rel (≤) t) : s.prod ≤ t.prod :=
begin
  induction h with _ _ _ _ rh _ rt,
  { refl },
  { rw [prod_cons, prod_cons],
    exact mul_le_mul' rh rt }
end

@[to_additive]
lemma prod_map_le_prod_map {s : multiset ι} (f : ι → α) (g : ι → α) (h : ∀ i, i ∈ s → f i ≤ g i) :
  (s.map f).prod ≤ (s.map g).prod :=
prod_le_prod_of_rel_le $ rel_map.2 $ rel_refl_of_refl_on h

@[to_additive]
lemma prod_map_le_prod (f : α → α) (h : ∀ x, x ∈ s → f x ≤ x) : (s.map f).prod ≤ s.prod :=
prod_le_prod_of_rel_le $ rel_map_left.2 $ rel_refl_of_refl_on h

@[to_additive]
lemma prod_le_prod_map (f : α → α) (h : ∀ x, x ∈ s → x ≤ f x) : s.prod ≤ (s.map f).prod :=
@prod_map_le_prod αᵒᵈ _ _ f h

@[to_additive card_nsmul_le_sum]
lemma pow_card_le_prod (h : ∀ x ∈ s, a ≤ x) : a ^ s.card ≤ s.prod :=
by { rw [←multiset.prod_replicate, ←multiset.map_const], exact prod_map_le_prod _ h }

end ordered_comm_monoid

lemma prod_nonneg [ordered_comm_semiring α] {m : multiset α} (h : ∀ a ∈ m, (0 : α) ≤ a) :
  0 ≤ m.prod :=
begin
  revert h,
  refine m.induction_on _ _,
  { rintro -, rw prod_zero, exact zero_le_one },
  intros a s hs ih,
  rw prod_cons,
  exact mul_nonneg (ih _ $ mem_cons_self _ _) (hs $ λ a ha, ih _ $ mem_cons_of_mem ha),
end

/-- Slightly more general version of `multiset.prod_eq_one_iff` for a non-ordered `monoid` -/
@[to_additive "Slightly more general version of `multiset.sum_eq_zero_iff`
  for a non-ordered `add_monoid`"]
lemma prod_eq_one [comm_monoid α] {m : multiset α} (h : ∀ x ∈ m, x = (1 : α)) : m.prod = 1 :=
begin
  induction m using quotient.induction_on with l,
  simp [list.prod_eq_one h],
end

@[to_additive]
lemma le_prod_of_mem [canonically_ordered_monoid α] {m : multiset α} {a : α} (h : a ∈ m) :
  a ≤ m.prod :=
begin
  obtain ⟨m', rfl⟩ := exists_cons_of_mem h,
  rw [prod_cons],
  exact _root_.le_mul_right (le_refl a),
end

@[to_additive le_sum_of_subadditive_on_pred]
lemma le_prod_of_submultiplicative_on_pred [comm_monoid α] [ordered_comm_monoid β]
  (f : α → β) (p : α → Prop) (h_one : f 1 = 1) (hp_one : p 1)
  (h_mul : ∀ a b, p a → p b → f (a * b) ≤ f a * f b)
  (hp_mul : ∀ a b, p a → p b → p (a * b)) (s : multiset α) (hps : ∀ a, a ∈ s → p a) :
  f s.prod ≤ (s.map f).prod :=
begin
  revert s,
  refine multiset.induction _ _,
  { simp [le_of_eq h_one] },
  intros a s hs hpsa,
  have hps : ∀ x, x ∈ s → p x, from λ x hx, hpsa x (mem_cons_of_mem hx),
  have hp_prod : p s.prod, from prod_induction p s hp_mul hp_one hps,
  rw [prod_cons, map_cons, prod_cons],
  exact (h_mul a s.prod (hpsa a (mem_cons_self a s)) hp_prod).trans (mul_le_mul_left' (hs hps) _),
end

@[to_additive le_sum_of_subadditive]
lemma le_prod_of_submultiplicative [comm_monoid α] [ordered_comm_monoid β]
  (f : α → β) (h_one : f 1 = 1) (h_mul : ∀ a b, f (a * b) ≤ f a * f b) (s : multiset α) :
  f s.prod ≤ (s.map f).prod :=
le_prod_of_submultiplicative_on_pred f (λ i, true) h_one trivial (λ x y _ _ , h_mul x y) (by simp)
  s (by simp)

@[to_additive le_sum_nonempty_of_subadditive_on_pred]

@[to_additive le_sum_nonempty_of_subadditive]
lemma le_prod_nonempty_of_submultiplicative [comm_monoid α] [ordered_comm_monoid β]
  (f : α → β) (h_mul : ∀ a b, f (a * b) ≤ f a * f b) (s : multiset α) (hs_nonempty : s ≠ ∅) :
  f s.prod ≤ (s.map f).prod :=
le_prod_nonempty_of_submultiplicative_on_pred f (λ i, true) (by simp [h_mul]) (by simp) s
  hs_nonempty (by simp)

@[simp] lemma sum_map_singleton (s : multiset α) : (s.map (λ a, ({a} : multiset α))).sum = s :=
multiset.induction_on s (by simp) (by simp)

lemma abs_sum_le_sum_abs [linear_ordered_add_comm_group α] {s : multiset α} :
  abs s.sum ≤ (s.map abs).sum :=
le_sum_of_subadditive _ abs_zero abs_add s

lemma sum_nat_mod (s : multiset ℕ) (n : ℕ) : s.sum % n = (s.map (% n)).sum % n :=
by induction s using multiset.induction; simp [nat.add_mod, *]

lemma prod_nat_mod (s : multiset ℕ) (n : ℕ) : s.prod % n = (s.map (% n)).prod % n :=
by induction s using multiset.induction; simp [nat.mul_mod, *]

lemma sum_int_mod (s : multiset ℤ) (n : ℤ) : s.sum % n = (s.map (% n)).sum % n :=
by induction s using multiset.induction; simp [int.add_mod, *]

lemma prod_int_mod (s : multiset ℤ) (n : ℤ) : s.prod % n = (s.map (% n)).prod % n :=
by induction s using multiset.induction; simp [int.mul_mod, *]

end multiset

@[to_additive]
lemma map_multiset_prod [comm_monoid α] [comm_monoid β] {F : Type*} [monoid_hom_class F α β]
  (f : F) (s : multiset α) : f s.prod = (s.map f).prod :=
(s.prod_hom f).symm

@[to_additive]
protected lemma monoid_hom.map_multiset_prod [comm_monoid α] [comm_monoid β] (f : α →* β)
  (s : multiset α) : f s.prod = (s.map f).prod :=
(s.prod_hom f).symm


-- ---------------------------------------------------------------------
-- Ejercicio 1. Realizar las siguientes acciones:
-- 1. Importar la teoría Definicion_de_funciones_acotadas
-- 2. Declarar f y g como variables de funciones de ℝ en ℝ.
-- 3. Declarar a y b como variables sobre ℝ.
-- ----------------------------------------------------------------------

import .Definicion_de_funciones_acotadas -- 1

variables {f g : ℝ → ℝ}                  -- 2
variables {a b : ℝ}                      -- 3

-- ---------------------------------------------------------------------
-- Ejercicio 2. Demostrar que si a es una cota inferior de f y b lo es
-- de g, entonces a + b lo es de f + g.
-- ----------------------------------------------------------------------

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

lemma fn_lb_add
  (hfa : fn_lb f a)
  (hgb : fn_lb g b)
  : fn_lb (f + g) (a + b) :=
begin
  intro x,
  change a + b ≤ f x + g x,
  apply add_le_add,
  apply hfa,
  apply hgb
end

-- Su desarrollo es
--
-- f g : ℝ → ℝ,
-- a b : ℝ,
-- hfa : fn_lb f a,
-- hgb : fn_lb g b
-- ⊢ fn_lb (λ (x : ℝ), f x + g x) (a + b)
--    >> intro x,
-- x : ℝ
-- ⊢ a + b ≤ (λ (x : ℝ), f x + g x) x
--    >> change a + b ≤ f x + g x,
-- ⊢ a + b ≤ f x + g x
--    >> apply add_le_add,
-- | ⊢ a ≤ f x
-- |    >> apply hfa,
-- | ⊢ b ≤ g x
-- |    >> apply hgb
-- no goals

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

example
  (hfa : fn_lb f a)
  (hgb : fn_lb g b)
  : fn_lb (f + g) (a + b) :=
λ x, add_le_add (hfa x) (hgb x)

-- ---------------------------------------------------------------------
-- Ejercicio 3. Demostrar que la suma de dos funciones acotadas
-- inferiormente también lo está.
-- ----------------------------------------------------------------------

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

example
  (lbf : fn_has_lb f)
  (lbg : fn_has_lb g)
  : fn_has_lb (f + g) :=
begin
  cases lbf with a ha,
  cases lbg with b hb,
  have h1 : fn_lb (f + g) (a + b) := fn_lb_add ha hb,
  have h2 : ∃ z, ∀ x, z ≤ (f + g) x :=
    by exact Exists.intro (a + b) h1,
  show fn_has_lb (f + g),
    by exact h2,
end

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

example
  (lbf : fn_has_lb f)
  (lbg : fn_has_lb g)
  : fn_has_lb (f + g) :=
begin
  cases lbf with a lbfa,
  cases lbg with b lbgb,
  use a + b,
  apply fn_lb_add lbfa lbgb,
end

-- Su desarrollo es
--
-- f g : ℝ → ℝ,
-- lbf : fn_has_lb f,
-- lbg : fn_has_lb g
-- ⊢ fn_has_lb (λ (x : ℝ), f x + g x)
--    >> cases lbf with a lbfa,
-- f g : ℝ → ℝ,
-- lbg : fn_has_lb g,
-- a : ℝ,
-- lbfa : fn_lb f a
-- ⊢ fn_has_lb (λ (x : ℝ), f x + g x)
--    >> cases lbg with b lbgb,
-- f g : ℝ → ℝ,
-- a : ℝ,
-- lbfa : fn_lb f a,
-- b : ℝ,
-- lbgb : fn_lb g b
-- ⊢ fn_has_lb (λ (x : ℝ), f x + g x)
--    >> use a + b,
-- ⊢ fn_lb (λ (x : ℝ), f x + g x) (a + b)
--    >> apply fn_lb_add lbfa lbgb
-- no goals

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

example
  (lbf : fn_has_lb f)
  (lbg : fn_has_lb g)
  : fn_has_lb (f + g) :=
begin
  rcases lbf with ⟨a, lbfa⟩,
  rcases lbg with ⟨b, lbfb⟩,
  exact ⟨a + b, fn_lb_add lbfa lbfb⟩,
end

-- 4ª demostración
-- ===============

example :
  fn_has_lb f → fn_has_lb g → fn_has_lb (f + g) :=
begin
  rintros ⟨a, lbfa⟩ ⟨b, lbfb⟩,
  exact ⟨a + b, fn_lb_add lbfa lbfb⟩,
end

-- 5ª demostración
-- ===============

example :
  fn_has_lb f → fn_has_lb g → fn_has_lb (f + g) :=
λ ⟨a, lbfa⟩ ⟨b, lbfb⟩, ⟨a + b, fn_lb_add lbfa lbfb⟩


-- Monotonía del conjunto potencia: 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B
-- ===================================================

import data.set
open set

variable  {U : Type}
variables {A B C : set U}

-- #reduce 𝒫 A
-- #reduce B ∈ 𝒫 A

-- ----------------------------------------------------
-- Ej. 1. Demostrar
--    𝒫 A ⊆ 𝒫 B → A ⊆ B
-- ----------------------------------------------------

-- 1ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
begin
  intro h,
  apply subset_of_mem_powerset,
  apply h,
  apply mem_powerset,
  exact subset.rfl,
end

-- 2ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
begin
  intro h,
  apply h,
  exact subset.rfl,
end

-- 3ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
begin
  intro h,
  exact (h subset.rfl),
end

-- 4ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
λ h, h subset.rfl

-- 5ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
assume h1 : 𝒫 A ⊆ 𝒫 B,
have h2 : A ⊆ A, from subset.rfl,
have h3 : A ∈ 𝒫 A, from h2,
have h4 : A ∈ 𝒫 B, from h1 h3,
show A ⊆ B, from h4

-- 6ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
assume h1 : 𝒫 A ⊆ 𝒫 B,
have h2 : A ⊆ A, from subset.rfl,
have h3 : A ∈ 𝒫 A, from h2,
h1 h3

-- 7ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
assume h1 : 𝒫 A ⊆ 𝒫 B,
have h2 : A ⊆ A, from subset.rfl,
h1 h2

-- 8ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
assume h1 : 𝒫 A ⊆ 𝒫 B,
h1 subset.rfl

-- 9ª demostración
lemma aux1 : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
λ h, h subset.rfl

-- 10ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
powerset_mono.mp

-- ----------------------------------------------------
-- Ej. 2. Demostrar
--    A ⊆ B → 𝒫 A ⊆ 𝒫 B
-- ----------------------------------------------------

-- 1ª demostración
example : A ⊆ B → 𝒫 A ⊆ 𝒫 B :=
begin
  intro h,
  intros C hCA,
  apply mem_powerset,
  apply subset.trans hCA h,
end

-- 2ª demostración
example : A ⊆ B → 𝒫 A ⊆ 𝒫 B :=
begin
  intros h C hCA,
  apply subset.trans hCA h,
end

-- 3ª demostración
lemma aux2 : A ⊆ B → 𝒫 A ⊆ 𝒫 B :=
λ h C hCA, subset.trans hCA h

-- 4ª demostración
example : A ⊆ B → 𝒫 A ⊆ 𝒫 B :=
powerset_mono.mpr

-- ----------------------------------------------------
-- Ej. 3. Demostrar
--    𝒫 A ⊆ 𝒫 B ↔ A ⊆ B
-- ----------------------------------------------------

-- 1ª demostración
example : 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B :=
iff.intro aux1 aux2

-- 2ª demostración
example : 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B :=
-- by library_search
powerset_mono

-- 3ª demostración
example : 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B :=
-- by hint
by finish

-- 4ª demostración
example : 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B :=
by simp


/- LoVe Exercise 13: Rational and Real Numbers -/

import .love05_inductive_predicates_demo
import .love13_rational_and_real_numbers_demo

namespace LoVe

set_option pp.beta true

/- Question 1: Rationals -/

/- 1.1. Prove the following lemma.

Hint: The lemma `fraction.mk.inj_eq` might be useful. -/

#check fraction.mk.inj_eq

lemma fraction.ext (a b : fraction) (h : fraction.num a = fraction.num b)
    (h': fraction.denom a = fraction.denom b) :
  a = b :=
sorry

/- 1.2. Extending the `fraction.has_mul` instance from the lecture, declare
`fraction` an instance of `semigroup`.

Hint: Use the lemma `fraction.ext` above, and possibly `fraction.mul_num`, and
`fraction.mul_denom`. -/

#check fraction.ext
#check fraction.mul_num
#check fraction.mul_denom

instance fraction.semigroup : semigroup fraction :=
{ mul_assoc :=
    sorry,
  ..fraction.has_mul }

/- 1.3. Extending the `myℚ.has_mul` instance from the lecture, declare `myℚ` an
instance of `semigroup`.

Hint: The lemma `quotient.induction_on₃` might be useful. -/

#check quotient.induction_on₃

instance myℚ.semigroup : semigroup myℚ :=
{ mul_assoc :=
    sorry,
  ..myℚ.has_mul }

/- Question 2: Structural Induction on Paper -/

/- This and the next question will exercise your understanding of induction,
especially if you need to perform induction proofs on a whiteboard (or on paper
at the exam).

Guidelines for paper proofs:

We expect detailed, rigorous, mathematical proofs. You are welcome to use
standard mathematical notation or Lean structured commands (e.g., `assume`,
`have`, `show`, `calc`). You can also use tactical proofs (e.g., `intro`,
`apply`), but then please indicate some of the intermediate goals, so that we
can follow the chain of reasoning.

Major proof steps, including applications of induction and invocation of the
induction hypothesis, must be stated explicitly. For each case of a proof by
induction, you must list the inductive hypotheses assumed (if any) and the goal
to be proved. Minor proof steps corresponding to `refl`, `simp`, or `linarith`
need not be justified if you think they are obvious (to humans), but you should
say which key lemmas they follow from. You should be explicit whenever you use a
function definition or an introduction rule for an inductive predicate. -/

/- 2.1. Recall the following inductive datatype for binary trees from lecture 4:

    inductive btree (α : Type) : Type
    | empty {} : btree
    | node     : α → btree → btree → btree

We defined a function `mirror` on these binary trees as follows:

    def mirror {α : Type} : btree α → btree α
    | empty        := empty
    | (node a l r) := node a (mirror r) (mirror l)

Prove the following lemma by structural induction, as a paper proof:

    lemma mirror_mirror {α : Type} :
      ∀t : btree α, mirror (mirror t) = t -/

-- enter your "paper" proof here

/- 2.2. Prove the same lemma in Lean and compare it with your paper proof. -/

lemma mirror_mirror₂ {α : Type} :
  ∀t : btree α, mirror (mirror t) = t :=
sorry

/- Question 3: Rule Induction on Paper -/

/- 3.1. Recall the following inductive predicate from lecture 5:

    inductive even : ℕ → Prop
    | zero    : even 0
    | add_two : ∀n, even n → even (n + 2)

Prove the following lemma by rule induction, as a paper proof, following the
guidelines given at the beginning of question 2. This is a good exercise to
develop a deeper understanding of how rule induction works (and is good
practice for the final exam).

    lemma exists_of_even (n : ℕ) (h : even n) :
      ∃k : ℕ, n = 2 * k -/

-- enter your "paper" proof here

/- 3.2. Prove the same lemma in Lean and compare it with your paper proof. -/

lemma exists_of_even (n : ℕ) (h : even n) :
  ∃k : ℕ, n = 2 * k :=
sorry

end LoVe


/-  Math40001 : Introduction to university mathematics.

Problem Sheet 4, October 2020.

This is a Lean file. It can be read with the Lean theorem prover.

You can work on this file online via the link at
https://github.com/ImperialCollegeLondon/M40001_lean/blob/master/README.md

or you can install Lean and its maths library following the
instructions at
https://leanprover-community.github.io/get_started.html

and then just clone the project onto your own computer
with `leanproject get ImperialCollegeLondon/M40001_lean`.

There are advantages to installing Lean on your own computer
(for example it's faster), but it's more hassle than
just using it online.

In the below, delete "sorry" and replace it with some
tactics which prove the result.

-/

import data.real.basic -- the real numbers

/- Question 1.

For each of the sets~$X$ and binary relations~$R$ below, figure out whether~$R$ is (a) reflexive, (b) symmetric, (c) antisymmetric, (d) transitive.

  \begin{enumerate}
  \item Let $X$ be the set $\{1,2\}$ and define~$R$ like this: $R(1,1)$ is true, $R(1,2)$ is true, $R(2,1)$ is true and $R(2,2)$ is false.
  \item Let~$X=\R$ and define $R(a,b)$ to be the proposition $a=-b$.
  \item Let~$X=\R$ and define $R(a,b)$ to be false for all real numbers~$a$ and~$b$.
  \item Let~$X$ be the empty set and define~$R$ to be the empty binary relation (we don't have to say what its value is on any pair $(a,b)$ because no such pairs exist).
  \end{enumerate}

-/

namespace Q1a

inductive X : Type
| one : X
| two : X

namespace X

def R : X → X → Prop
| one one := true
| one two := true
| two one := true
| two two := false

-- insert "¬" if you think it's not reflexive
lemma Q1a_refl : reflexive R :=
begin
  sorry
end

-- insert "¬" if you think it's not symmetric
lemma Q1a_symm : symmetric R :=
begin
  sorry
end

-- insert "¬" if you think it's not transitive
lemma Q1a_trans : transitive R :=
begin
  sorry
end

-- insert "¬" if you think it's not an equiv reln
lemma Q1a_equiv : equivalence R :=
begin
  sorry
end

end X
end Q1a

namespace Q1b

def R (a b : ℝ) : Prop := a = -b

-- insert "¬" if you think it's not reflexive
lemma Q1b_refl : reflexive R :=
begin
  sorry
end

-- insert "¬" if you think it's not symmetric
lemma Q1b_symm : symmetric R :=
begin
  sorry
end

-- insert "¬" if you think it's not transitive
lemma Q1b_trans : transitive R :=
begin
  sorry
end

-- insert "¬" if you think it's not an equiv reln
lemma Q1b_equiv : equivalence R :=
begin
  sorry
end

end Q1b

namespace Q1c

def R (a b : ℝ) : Prop := false

-- insert "¬" if you think it's not reflexive
lemma Q1c_refl : reflexive R :=
begin
  sorry
end

-- insert "¬" if you think it's not symmetric
lemma Q1c_symm : symmetric R :=
begin
  sorry
end

-- insert "¬" if you think it's not transitive
lemma Q1c_trans : transitive R :=
begin
  sorry
end

-- insert "¬" if you think it's not an equiv reln
lemma Q1c_equiv : equivalence R :=
begin
  sorry
end

end Q1c

namespace Q1d

def R (a b : empty) : Prop := by cases a -- i.e. "I'll define it in all cases -- oh look there are no cases"

-- insert "¬" if you think it's not reflexive
lemma Q1d_refl : reflexive R :=
begin
  sorry
end

-- insert "¬" if you think it's not symmetric
lemma Q1d_symm : symmetric R :=
begin
  sorry
end

-- insert "¬" if you think it's not transitive
lemma Q1d_trans : transitive R :=
begin
  sorry
end

-- insert "¬" if you think it's not an equiv reln
lemma Q1d_equiv : equivalence R :=
begin
  sorry
end

end Q1d

-- `set ℤ` is the type of subsets of the integers.

def Q2a : partial_order (set ℤ) :=
{ le := λ A B, A ⊆ B,
  le_refl := begin sorry end,
  le_antisymm := begin sorry end,
  le_trans := begin sorry end
   }

-- insert ¬ at the beginning if you think it's wrong
lemma Q2b : is_total (set ℤ) (λ A B, A ⊆ B) :=
begin
  sorry
end

-- put ¬ in front if you think it's wrong
lemma Q3a : symmetric (λ a b : ℝ, a < b) :=
begin
  sorry
end

-- put ¬ in front if you think it's wrong
lemma Q3b : symmetric (λ a b : (∅ : set ℝ), a < b) :=
begin
  sorry
end

-- type in the proof in the question. Where do you get stuck?
lemma Q4 (X : Type) (R : X → (X → Prop)) (hs : symmetric R) (ht : transitive R) : reflexive R :=
begin
  sorry
end

open function

definition pals {X Y Z : Type} (f : X → Y) (g : X → Z) := ∃ h : Y → Z, bijective h ∧ g = h ∘ f

lemma Q5 (X Y Z: Type) (f : X → Y) (g : X → Z) (hf : surjective f) (hg : surjective g) : pals f g ↔ ∀ a b : X, (f a = f b) ↔ (g a = g b) :=
begin
  sorry
end