import Mathlib.Algebra.Star.Unitary import Mathlib.Data.Nat.ModEq import Mathlib.NumberTheory.Zsqrtd.Basic import Mathlib.Tactic.Monotonicity import Mathlib.Algebra.GroupPower.Order #align_import number_theory.pell_matiyasevic from "leanprover-community/mathlib"@"795b501869b9fa7aa716d5fdadd00c03f983a605" /-! # Pell's equation and Matiyasevic's theorem This file solves Pell's equation, i.e. integer solutions to `x ^ 2 - d * y ^ 2 = 1` *in the special case that `d = a ^ 2 - 1`*. This is then applied to prove Matiyasevic's theorem that the power function is Diophantine, which is the last key ingredient in the solution to Hilbert's tenth problem. For the definition of Diophantine function, see `NumberTheory.Dioph`. For results on Pell's equation for arbitrary (positive, non-square) `d`, see `NumberTheory.Pell`. ## Main definition * `pell` is a function assigning to a natural number `n` the `n`-th solution to Pell's equation constructed recursively from the initial solution `(0, 1)`. ## Main statements * `eq_pell` shows that every solution to Pell's equation is recursively obtained using `pell` * `matiyasevic` shows that a certain system of Diophantine equations has a solution if and only if the first variable is the `x`-component in a solution to Pell's equation - the key step towards Hilbert's tenth problem in Davis' version of Matiyasevic's theorem. * `eq_pow_of_pell` shows that the power function is Diophantine. ## Implementation notes The proof of Matiyasevic's theorem doesn't follow Matiyasevic's original account of using Fibonacci numbers but instead Davis' variant of using solutions to Pell's equation. ## References * [M. Carneiro, _A Lean formalization of Matiyasevič's theorem_][carneiro2018matiyasevic] * [M. Davis, _Hilbert's tenth problem is unsolvable_][MR317916] ## Tags Pell's equation, Matiyasevic's theorem, Hilbert's tenth problem -/ namespace Pell open Nat section variable {d : ℤ} /-- The property of being a solution to the Pell equation, expressed as a property of elements of `ℤ√d`. -/ def IsPell : ℤ√d → Prop | ⟨x, y⟩ => x * x - d * y * y = 1 #align pell.is_pell Pell.IsPell theorem isPell_norm : ∀ {b : ℤ√d}, IsPell b ↔ b * star b = 1 | ⟨x, y⟩ => by simp [Zsqrtd.ext_iff, IsPell, mul_comm]; ring_nf #align pell.is_pell_norm Pell.isPell_norm theorem isPell_iff_mem_unitary : ∀ {b : ℤ√d}, IsPell b ↔ b ∈ unitary (ℤ√d) | ⟨x, y⟩ => by rw [unitary.mem_iff, isPell_norm, mul_comm (star _), and_self_iff] #align pell.is_pell_iff_mem_unitary Pell.isPell_iff_mem_unitary theorem isPell_mul {b c : ℤ√d} (hb : IsPell b) (hc : IsPell c) : IsPell (b * c) := isPell_norm.2 (by simp [mul_comm, mul_left_comm c, mul_assoc, star_mul, isPell_norm.1 hb, isPell_norm.1 hc]) #align pell.is_pell_mul Pell.isPell_mul theorem isPell_star : ∀ {b : ℤ√d}, IsPell b ↔ IsPell (star b) | ⟨x, y⟩ => by simp [IsPell, Zsqrtd.star_mk] #align pell.is_pell_star Pell.isPell_star end section -- Porting note: was parameter in Lean3 variable {a : ℕ} (a1 : 1 < a) private def d (_a1 : 1 < a) := a * a - 1 @[simp] theorem d_pos : 0 < d a1 := tsub_pos_of_lt (mul_lt_mul a1 (le_of_lt a1) (by decide) (Nat.zero_le _) : 1 * 1 < a * a) #align pell.d_pos Pell.d_pos -- TODO(lint): Fix double namespace issue /-- The Pell sequences, i.e. the sequence of integer solutions to `x ^ 2 - d * y ^ 2 = 1`, where `d = a ^ 2 - 1`, defined together in mutual recursion. -/ --@[nolint dup_namespace] def pell : ℕ → ℕ × ℕ -- Porting note: used pattern matching because `Nat.recOn` is noncomputable | 0 => (1, 0) | n+1 => ((pell n).1 * a + d a1 * (pell n).2, (pell n).1 + (pell n).2 * a) #align pell.pell Pell.pell /-- The Pell `x` sequence. -/ def xn (n : ℕ) : ℕ := (pell a1 n).1 #align pell.xn Pell.xn /-- The Pell `y` sequence. -/ def yn (n : ℕ) : ℕ := (pell a1 n).2 #align pell.yn Pell.yn @[simp] theorem pell_val (n : ℕ) : pell a1 n = (xn a1 n, yn a1 n) := show pell a1 n = ((pell a1 n).1, (pell a1 n).2) from match pell a1 n with | (_, _) => rfl #align pell.pell_val Pell.pell_val @[simp] theorem xn_zero : xn a1 0 = 1 := rfl #align pell.xn_zero Pell.xn_zero @[simp] theorem yn_zero : yn a1 0 = 0 := rfl #align pell.yn_zero Pell.yn_zero @[simp] theorem xn_succ (n : ℕ) : xn a1 (n + 1) = xn a1 n * a + d a1 * yn a1 n := rfl #align pell.xn_succ Pell.xn_succ @[simp] theorem yn_succ (n : ℕ) : yn a1 (n + 1) = xn a1 n + yn a1 n * a := rfl #align pell.yn_succ Pell.yn_succ --@[simp] Porting note (#10618): `simp` can prove it theorem xn_one : xn a1 1 = a := by simp #align pell.xn_one Pell.xn_one --@[simp] Porting note (#10618): `simp` can prove it theorem yn_one : yn a1 1 = 1 := by simp #align pell.yn_one Pell.yn_one /-- The Pell `x` sequence, considered as an integer sequence.-/ def xz (n : ℕ) : ℤ := xn a1 n #align pell.xz Pell.xz /-- The Pell `y` sequence, considered as an integer sequence.-/ def yz (n : ℕ) : ℤ := yn a1 n #align pell.yz Pell.yz section /-- The element `a` such that `d = a ^ 2 - 1`, considered as an integer.-/ def az (a : ℕ) : ℤ := a #align pell.az Pell.az end theorem asq_pos : 0 < a * a := le_trans (le_of_lt a1) (by have := @Nat.mul_le_mul_left 1 a a (le_of_lt a1); rwa [mul_one] at this) #align pell.asq_pos Pell.asq_pos theorem dz_val : ↑(d a1) = az a * az a - 1 := have : 1 ≤ a * a := asq_pos a1 by rw [Pell.d, Int.ofNat_sub this]; rfl #align pell.dz_val Pell.dz_val @[simp] theorem xz_succ (n : ℕ) : (xz a1 (n + 1)) = xz a1 n * az a + d a1 * yz a1 n := rfl #align pell.xz_succ Pell.xz_succ @[simp] theorem yz_succ (n : ℕ) : yz a1 (n + 1) = xz a1 n + yz a1 n * az a := rfl #align pell.yz_succ Pell.yz_succ /-- The Pell sequence can also be viewed as an element of `ℤ√d` -/ def pellZd (n : ℕ) : ℤ√(d a1) := ⟨xn a1 n, yn a1 n⟩ #align pell.pell_zd Pell.pellZd @[simp] theorem pellZd_re (n : ℕ) : (pellZd a1 n).re = xn a1 n := rfl #align pell.pell_zd_re Pell.pellZd_re @[simp] theorem pellZd_im (n : ℕ) : (pellZd a1 n).im = yn a1 n := rfl #align pell.pell_zd_im Pell.pellZd_im theorem isPell_nat {x y : ℕ} : IsPell (⟨x, y⟩ : ℤ√(d a1)) ↔ x * x - d a1 * y * y = 1 := ⟨fun h => Nat.cast_inj.1 (by rw [Int.ofNat_sub (Int.le_of_ofNat_le_ofNat <| Int.le.intro_sub _ h)]; exact h), fun h => show ((x * x : ℕ) - (d a1 * y * y : ℕ) : ℤ) = 1 by rw [← Int.ofNat_sub <| le_of_lt <| Nat.lt_of_sub_eq_succ h, h]; rfl⟩ #align pell.is_pell_nat Pell.isPell_nat @[simp] theorem pellZd_succ (n : ℕ) : pellZd a1 (n + 1) = pellZd a1 n * ⟨a, 1⟩ := by ext <;> simp #align pell.pell_zd_succ Pell.pellZd_succ theorem isPell_one : IsPell (⟨a, 1⟩ : ℤ√(d a1)) := show az a * az a - d a1 * 1 * 1 = 1 by simp [dz_val] #align pell.is_pell_one Pell.isPell_one theorem isPell_pellZd : ∀ n : ℕ, IsPell (pellZd a1 n) | 0 => rfl | n + 1 => by let o := isPell_one a1 simp; exact Pell.isPell_mul (isPell_pellZd n) o #align pell.is_pell_pell_zd Pell.isPell_pellZd @[simp] theorem pell_eqz (n : ℕ) : xz a1 n * xz a1 n - d a1 * yz a1 n * yz a1 n = 1 := isPell_pellZd a1 n #align pell.pell_eqz Pell.pell_eqz @[simp] theorem pell_eq (n : ℕ) : xn a1 n * xn a1 n - d a1 * yn a1 n * yn a1 n = 1 := let pn := pell_eqz a1 n have h : (↑(xn a1 n * xn a1 n) : ℤ) - ↑(d a1 * yn a1 n * yn a1 n) = 1 := by repeat' rw [Int.ofNat_mul]; exact pn have hl : d a1 * yn a1 n * yn a1 n ≤ xn a1 n * xn a1 n := Nat.cast_le.1 <| Int.le.intro _ <| add_eq_of_eq_sub' <| Eq.symm h Nat.cast_inj.1 (by rw [Int.ofNat_sub hl]; exact h) #align pell.pell_eq Pell.pell_eq instance dnsq : Zsqrtd.Nonsquare (d a1) := ⟨fun n h => have : n * n + 1 = a * a := by rw [← h]; exact Nat.succ_pred_eq_of_pos (asq_pos a1) have na : n < a := Nat.mul_self_lt_mul_self_iff.1 (by rw [← this]; exact Nat.lt_succ_self _) have : (n + 1) * (n + 1) ≤ n * n + 1 := by rw [this]; exact Nat.mul_self_le_mul_self na have : n + n ≤ 0 := @Nat.le_of_add_le_add_right _ (n * n + 1) _ (by ring_nf at this ⊢; assumption) Nat.ne_of_gt (d_pos a1) <| by rwa [Nat.eq_zero_of_le_zero ((Nat.le_add_left _ _).trans this)] at h⟩ #align pell.dnsq Pell.dnsq theorem xn_ge_a_pow : ∀ n : ℕ, a ^ n ≤ xn a1 n | 0 => le_refl 1 | n + 1 => by simp only [_root_.pow_succ, xn_succ] exact le_trans (Nat.mul_le_mul_right _ (xn_ge_a_pow n)) (Nat.le_add_right _ _) #align pell.xn_ge_a_pow Pell.xn_ge_a_pow theorem n_lt_a_pow : ∀ n : ℕ, n < a ^ n | 0 => Nat.le_refl 1 | n + 1 => by have IH := n_lt_a_pow n have : a ^ n + a ^ n ≤ a ^ n * a := by rw [← mul_two] exact Nat.mul_le_mul_left _ a1 simp only [_root_.pow_succ, gt_iff_lt] refine' lt_of_lt_of_le _ this exact add_lt_add_of_lt_of_le IH (lt_of_le_of_lt (Nat.zero_le _) IH) #align pell.n_lt_a_pow Pell.n_lt_a_pow