Module docstring
{"# Properties of the natural number square root function.
","### sqrt
See [Wikipedia, Methods of computing square roots] (https://en.wikipedia.org/wiki/Methodsofcomputingsquareroots#Binarynumeralsystem(base2)). "}
{"# Properties of the natural number square root function.
","### sqrt
See [Wikipedia, Methods of computing square roots] (https://en.wikipedia.org/wiki/Methodsofcomputingsquareroots#Binarynumeralsystem(base2)). "}
Nat.sqrt.iter_sq_le
      theorem
     (n guess : ℕ) : sqrt.iter n guess * sqrt.iter n guess ≤ n
        lemma sqrt.iter_sq_le (n guess : ℕ) : sqrt.iter n guess * sqrt.iter n guess ≤ n := by
  unfold sqrt.iter
  let next := (guess + n / guess) / 2
  if h : next < guess then
    simpa only [next, dif_pos h] using sqrt.iter_sq_le n next
  else
    simp only [next, dif_neg h]
    apply Nat.mul_le_of_le_div
    apply Nat.le_of_add_le_add_left (a := guess)
    rw [← Nat.mul_two, ← le_div_iff_mul_le]
    · exact Nat.le_of_not_lt h
    · exact Nat.zero_lt_two
        Nat.sqrt.lt_iter_succ_sq
      theorem
     (n guess : ℕ) (hn : n < (guess + 1) * (guess + 1)) : n < (sqrt.iter n guess + 1) * (sqrt.iter n guess + 1)
        lemma sqrt.lt_iter_succ_sq (n guess : ℕ) (hn : n < (guess + 1) * (guess + 1)) :
    n < (sqrt.iter n guess + 1) * (sqrt.iter n guess + 1) := by
  unfold sqrt.iter
  -- m was `next`
  let m := (guess + n / guess) / 2
  dsimp
  split_ifs with h
  · suffices n < (m + 1) * (m + 1) by
      simpa only [dif_pos h] using sqrt.lt_iter_succ_sq n m this
    refine Nat.lt_of_mul_lt_mul_left ?_ (a := 4 * (guess * guess))
    apply Nat.lt_of_le_of_lt AM_GM
    rw [show (4 : ℕ) = 2 * 2 from rfl]
    rw [Nat.mul_mul_mul_comm 2, Nat.mul_mul_mul_comm (2 * guess)]
    refine Nat.mul_self_lt_mul_self (?_ : _ < _ * ((_ / 2) + 1))
    rw [← add_div_right _ (by decide), Nat.mul_comm 2, Nat.mul_assoc,
      show guess + n / guess + 2 = (guess + n / guess + 1) + 1 from rfl]
    have aux_lemma {a : ℕ} : a ≤ 2 * ((a + 1) / 2) := by omega
    refine lt_of_lt_of_le ?_ (Nat.mul_le_mul_left _ aux_lemma)
    rw [Nat.add_assoc, Nat.mul_add]
    exact Nat.add_lt_add_left (lt_mul_div_succ _ (lt_of_le_of_lt (Nat.zero_le m) h)) _
  · simpa only [dif_neg h] using hn
        Nat.sqrt_le
      theorem
     (n : ℕ) : sqrt n * sqrt n ≤ n
        
      Nat.sqrt_le'
      theorem
     (n : ℕ) : sqrt n ^ 2 ≤ n
        lemma sqrt_le' (n : ℕ) : sqrt n ^ 2 ≤ n := by simpa [Nat.pow_two] using sqrt_le n
        Nat.lt_succ_sqrt
      theorem
     (n : ℕ) : n < succ (sqrt n) * succ (sqrt n)
        lemma lt_succ_sqrt (n : ℕ) : n < succ (sqrt n) * succ (sqrt n) := (sqrt_isSqrt n).right
        Nat.lt_succ_sqrt'
      theorem
     (n : ℕ) : n < succ (sqrt n) ^ 2
        lemma lt_succ_sqrt' (n : ℕ) : n < succ (sqrt n) ^ 2 := by simpa [Nat.pow_two] using lt_succ_sqrt n
        Nat.sqrt_le_add
      theorem
     (n : ℕ) : n ≤ sqrt n * sqrt n + sqrt n + sqrt n
        lemma sqrt_le_add (n : ℕ) : n ≤ sqrt n * sqrt n + sqrt n + sqrt n := by
  rw [← succ_mul]; exact le_of_lt_succ (lt_succ_sqrt n)
        Nat.le_sqrt
      theorem
     : m ≤ sqrt n ↔ m * m ≤ n
        
      Nat.le_sqrt'
      theorem
     : m ≤ sqrt n ↔ m ^ 2 ≤ n
        lemma le_sqrt' : m ≤ sqrt n ↔ m ^ 2 ≤ n := by simpa only [Nat.pow_two] using le_sqrt
        Nat.sqrt_lt
      theorem
     : sqrt m < n ↔ m < n * n
        lemma sqrt_lt : sqrtsqrt m < n ↔ m < n * n := by simp only [← not_le, le_sqrt]
        Nat.sqrt_lt'
      theorem
     : sqrt m < n ↔ m < n ^ 2
        lemma sqrt_lt' : sqrtsqrt m < n ↔ m < n ^ 2 := by simp only [← not_le, le_sqrt']
        Nat.sqrt_le_self
      theorem
     (n : ℕ) : sqrt n ≤ n
        lemma sqrt_le_self (n : ℕ) : sqrt n ≤ n := le_trans (le_mul_self _) (sqrt_le n)
        Nat.sqrt_le_sqrt
      theorem
     (h : m ≤ n) : sqrt m ≤ sqrt n
        lemma sqrt_le_sqrt (h : m ≤ n) : sqrt m ≤ sqrt n := le_sqrt.2 (le_trans (sqrt_le _) h)
        Nat.sqrt_zero
      theorem
     : sqrt 0 = 0
        
      Nat.sqrt_one
      theorem
     : sqrt 1 = 1
        
      Nat.sqrt_eq_zero
      theorem
     : sqrt n = 0 ↔ n = 0
        
      Nat.le_three_of_sqrt_eq_one
      theorem
     (h : sqrt n = 1) : n ≤ 3
        lemma le_three_of_sqrt_eq_one (h : sqrt n = 1) : n ≤ 3 :=
  le_of_lt_succ <| (@sqrt_lt n 2).1 <| by rw [h]; decide
        Nat.sqrt_lt_self
      theorem
     (h : 1 < n) : sqrt n < n
        lemma sqrt_lt_self (h : 1 < n) : sqrt n < n :=
  sqrt_lt.2 <| by have := Nat.mul_lt_mul_of_pos_left h (lt_of_succ_lt h); rwa [Nat.mul_one] at this
        Nat.sqrt_pos
      theorem
     : 0 < sqrt n ↔ 0 < n
        lemma sqrt_pos : 0 < sqrt n ↔ 0 < n :=
  le_sqrt
        Nat.sqrt_add_eq
      theorem
     (n : ℕ) (h : a ≤ n + n) : sqrt (n * n + a) = n
        lemma sqrt_add_eq (n : ℕ) (h : a ≤ n + n) : sqrt (n * n + a) = n :=
  le_antisymm
    (le_of_lt_succ <|
      sqrt_lt.2 <| by
        rw [succ_mul, mul_succ, add_succ, Nat.add_assoc]
        exact lt_succ_of_le (Nat.add_le_add_left h _))
    (le_sqrt.2 <| Nat.le_add_right _ _)
        Nat.sqrt_add_eq'
      theorem
     (n : ℕ) (h : a ≤ n + n) : sqrt (n ^ 2 + a) = n
        lemma sqrt_add_eq' (n : ℕ) (h : a ≤ n + n) : sqrt (n ^ 2 + a) = n := by
  simpa [Nat.pow_two] using sqrt_add_eq n h
        Nat.sqrt_eq
      theorem
     (n : ℕ) : sqrt (n * n) = n
        lemma sqrt_eq (n : ℕ) : sqrt (n * n) = n := sqrt_add_eq n (zero_le _)
        Nat.sqrt_eq'
      theorem
     (n : ℕ) : sqrt (n ^ 2) = n
        lemma sqrt_eq' (n : ℕ) : sqrt (n ^ 2) = n := sqrt_add_eq' n (zero_le _)
        Nat.sqrt_succ_le_succ_sqrt
      theorem
     (n : ℕ) : sqrt n.succ ≤ n.sqrt.succ
        lemma sqrt_succ_le_succ_sqrt (n : ℕ) : sqrt n.succ ≤ n.sqrt.succ :=
  le_of_lt_succ <| sqrt_lt.2 <| lt_succ_of_le <|
  succ_le_succ <| le_trans (sqrt_le_add n) <| Nat.add_le_add_right
    (by refine add_le_add (Nat.mul_le_mul_right _ ?_) ?_ <;> exact Nat.le_add_right _ 2) _
        Nat.exists_mul_self
      theorem
     (x : ℕ) : (∃ n, n * n = x) ↔ sqrt x * sqrt x = x
        lemma exists_mul_self (x : ℕ) : (∃ n, n * n = x) ↔ sqrt x * sqrt x = x :=
  ⟨fun ⟨n, hn⟩ ↦ by rw [← hn, sqrt_eq], fun h ↦ ⟨sqrt x, h⟩⟩
        Nat.exists_mul_self'
      theorem
     (x : ℕ) : (∃ n, n ^ 2 = x) ↔ sqrt x ^ 2 = x
        lemma exists_mul_self' (x : ℕ) : (∃ n, n ^ 2 = x) ↔ sqrt x ^ 2 = x := by
  simpa only [Nat.pow_two] using exists_mul_self x
        Nat.sqrt_mul_sqrt_lt_succ
      theorem
     (n : ℕ) : sqrt n * sqrt n < n + 1
        lemma sqrt_mul_sqrt_lt_succ (n : ℕ) : sqrt n * sqrt n < n + 1 :=
  Nat.lt_succ_iff.mpr (sqrt_le _)
        Nat.sqrt_mul_sqrt_lt_succ'
      theorem
     (n : ℕ) : sqrt n ^ 2 < n + 1
        lemma sqrt_mul_sqrt_lt_succ' (n : ℕ) : sqrt n ^ 2 < n + 1 :=
  Nat.lt_succ_iff.mpr (sqrt_le' _)
        Nat.succ_le_succ_sqrt
      theorem
     (n : ℕ) : n + 1 ≤ (sqrt n + 1) * (sqrt n + 1)
        lemma succ_le_succ_sqrt (n : ℕ) : n + 1 ≤ (sqrt n + 1) * (sqrt n + 1) :=
  le_of_pred_lt (lt_succ_sqrt _)
        Nat.succ_le_succ_sqrt'
      theorem
     (n : ℕ) : n + 1 ≤ (sqrt n + 1) ^ 2
        lemma succ_le_succ_sqrt' (n : ℕ) : n + 1 ≤ (sqrt n + 1) ^ 2 :=
  le_of_pred_lt (lt_succ_sqrt' _)
        Nat.not_exists_sq
      theorem
     (hl : m * m < n) (hr : n < (m + 1) * (m + 1)) : ¬∃ t, t * t = n
        /-- There are no perfect squares strictly between m² and (m+1)² -/
lemma not_exists_sq (hl : m * m < n) (hr : n < (m + 1) * (m + 1)) : ¬∃ t, t * t = n := by
  rintro ⟨t, rfl⟩
  have h1 : m < t := Nat.mul_self_lt_mul_self_iff.1 hl
  have h2 : t < m + 1 := Nat.mul_self_lt_mul_self_iff.1 hr
  exact (not_lt_of_ge <| le_of_lt_succ h2) h1
        Nat.not_exists_sq'
      theorem
     : m ^ 2 < n → n < (m + 1) ^ 2 → ¬∃ t, t ^ 2 = n
        lemma not_exists_sq' : m ^ 2 < n → n < (m + 1) ^ 2 → ¬∃ t, t ^ 2 = n := by
  simpa only [Nat.pow_two] using not_exists_sq