def _pell_small_minimal_positive_solutions(D, N): if D <= 0 or is_square(D) or D * D <= N: raise ValueError( "pell_small: Must have D > 0 not a perfect square and N < sqrt(D)") contfrac = QuadraticIrrational(D) terms = [] # Otherwise, solutions always exist for D not a perfect square. for (x, y) in contfrac.convergents(): if x * x - D * y * y == 1: break terms.append((x, y)) sols = [] for (x, y) in terms: k = x * x - D * y * y if N % k != 0: continue f = is_square(N // k) if f is not False: sols.append((f * x, f * y)) sols.sort() return sols
def test_pure_sqrt1(self): # Example 5.10 from "Fundamental Number Theory with Applications" by Mollin. cf = QuadraticIrrational(425) self.assertEqual(str(cf), 'Continued Fraction Expansion of sqrt(425)') self.assertEqual((cf.d, cf.p, cf.q), (425, 0, 1)) self.assertEqual(cf.pre_period, [20]) self.assertEqual(cf.fundamental_period, [1, 1, 1, 1, 1, 1, 40]) self.assertEqual(list(itertools.islice(cf.convergents(), 6)), [(20, 1), (21, 1), (41, 2), (62, 3), (103, 5), (165, 8)])
def test_pure_sqrt1(self): # Example 5.10 from "Fundamental Number Theory with Applications" by Mollin. cf = QuadraticIrrational(425) self.assertEqual(str(cf), "Continued Fraction Expansion of sqrt(425)") self.assertEqual((cf.d, cf.p, cf.q), (425, 0, 1)) self.assertEqual(cf.pre_period, [20]) self.assertEqual(cf.fundamental_period, [1, 1, 1, 1, 1, 1, 40]) self.assertEqual( list(itertools.islice(cf.convergents(), 6)), [(20, 1), (21, 1), (41, 2), (62, 3), (103, 5), (165, 8)] )
def test_quadratic_irrational1(self): # Example 5.8 from "Fundamental Number Theory with Applications" by Mollin. cf = QuadraticIrrational(22, 4, 6) self.assertEqual(str(cf), 'Continued Fraction Expansion of (4 + sqrt(22))/6') self.assertEqual((cf.d, cf.p, cf.q), (22, 4, 6)) self.assertEqual(cf.fundamental_period, [1, 2, 4, 2, 1, 8]) self.assertEqual(cf.pre_period, []) self.assertEqual(cf.period_length, 6) self.assertEqual(list(itertools.islice(cf.convergents(), 6)), [(1, 1), (3, 2), (13, 9), (29, 20), (42, 29), (365, 252)])
def test_quadratic_irrational1(self): # Example 5.8 from "Fundamental Number Theory with Applications" by Mollin. cf = QuadraticIrrational(22, 4, 6) self.assertEqual(str(cf), "Continued Fraction Expansion of (4 + sqrt(22))/6") self.assertEqual((cf.d, cf.p, cf.q), (22, 4, 6)) self.assertEqual(cf.fundamental_period, [1, 2, 4, 2, 1, 8]) self.assertEqual(cf.pre_period, []) self.assertEqual(cf.period_length, 6) self.assertEqual( list(itertools.islice(cf.convergents(), 6)), [(1, 1), (3, 2), (13, 9), (29, 20), (42, 29), (365, 252)] )
def test_quadratic_irrational5(self): # This example is one where (D - P*P) is not divisible by Q, and so they # must be modified. cf = QuadraticIrrational(10, 1, 2) self.assertEqual(str(cf), 'Continued Fraction Expansion of (1 + sqrt(10))/2') self.assertEqual((cf.d, cf.p, cf.q), (10, 1, 2)) self.assertEqual(cf.pre_period, [2]) self.assertEqual(cf.fundamental_period, [12, 3]) self.assertEqual(cf.period_length, 2) self.assertEqual(list(itertools.islice(cf.convergents(), 6)), [(2, 1), (25, 12), (77, 37), (949, 456), (2924, 1405), (36037, 17316)])
def pell_small(D, N): if D <= 0 or is_square(D) or D * D <= N: raise ValueError( "pell_small: Must have D > 0 not a perfect square and N < sqrt(D)") contfrac = QuadraticIrrational(D) # Otherwise, solutions always exist for D not a perfect square. for (x, y) in contfrac.convergents(): print(x, y) if x * x - D * y * y == N: yield (x, y)
def test_quadratic_irrational5(self): # This example is one where (D - P*P) is not divisible by Q, and so they # must be modified. cf = QuadraticIrrational(10, 1, 2) self.assertEqual(str(cf), "Continued Fraction Expansion of (1 + sqrt(10))/2") self.assertEqual((cf.d, cf.p, cf.q), (10, 1, 2)) self.assertEqual(cf.pre_period, [2]) self.assertEqual(cf.fundamental_period, [12, 3]) self.assertEqual(cf.period_length, 2) self.assertEqual( list(itertools.islice(cf.convergents(), 6)), [(2, 1), (25, 12), (77, 37), (949, 456), (2924, 1405), (36037, 17316)], )
def pell_fundamental_solution(D, n=1): """Returns the fundamental solution to the Pell equation x**2 - D*y**2 == n, where n in (-1, 1). Given D > 0 not a square, and n in (-1, 1), this method returns the fundamental solution to the Pell equation described above. The fundamental solution (x, y) is the one with least positive value of x, and correspondingly the least positive value of y. Input: * D: int (D > 0) * n: int (n in (-1, 1)). Returns: * (x, y): tuple Raises: * ValueError: If D <= 0, D is perfect square, n not in (-1, 1), or if no solutions exist. Examples: >>> pell_fundamental_solution(61) (1766319049, 226153980) >>> 1766319049**2 - 61*226153980**2 1 >>> pell_fundamental_solution(17, -1) (4, 1) >>> 4**2 - 17*1**2 -1 >>> pell_fundamental_solution(15, -1) Traceback (most recent call last): ... ValueError: pell_fundamental_solution: Solution nonexistent. >>> pell_fundamental_solution(15, -2) Traceback (most recent call last): ... ValueError: pell_fundamental_solution: Must have D > 0 not a perfect square and n in (-1, 1). Details: For D > 0 not a perfect square, the equation x**2 - D*y**2 == 1 always has solutions, while the equation x**2 - D*y**2 == -1 only has solutions when the continued fraction expansion of sqrt(D) has odd period length. See Corollary 5.7 of "Fundamental Number Theory with Applications" by Mollin for details. See also the article "Simple Continued Fraction Solutions for Diophantine Equations" by Mollin. """ if D <= 0 or is_square(D) or n not in (1, -1): raise ValueError("pell_fundamental_solution: Must have D > 0 not a perfect square and n in (-1, 1).") contfrac = QuadraticIrrational(D) # No solution for n == -1 if the period length is even. if n == -1 and contfrac.period_length % 2 == 0: raise ValueError("pell_fundamental_solution: Solution nonexistent.") # Otherwise, solutions always exist for D not a perfect square. for (x, y) in contfrac.convergents(): if x*x - D*y*y == n: return (x, y)
def pell_fundamental_solution(D, n=1): r"""Returns the fundamental solution to the Pell equation x**2 - D*y**2 == n, where n in (-1, 1). Given D > 0 not a square, and n in (-1, 1), this method returns the fundamental solution to the Pell equation described above. The fundamental solution (x, y) is the one with least positive value of x, and correspondingly the least positive value of y. Parameters ---------- D : int (D > 0) n : int (n == 1 or n == -1) Returns ------- (x, y): tuple Raises ------ ValueError : If D <= 0, D is perfect square, n not in (-1, 1), or if no solution exists. Notes ----- If D is a positive integer that is not a perfect square, then the equation x**2 - D*y**2 == 1 has a solution in positive integers. On the other hand, of the continued fraction expansion of sqrt(D) has even period length, then there are no solutions to the equation x**2 - D*y**2 == -1. Otherwise, the equation has a solution. See Theorem 3.2.1 in [1] for the positive case and Theorem 3.6.1 in [1] for the negative case. References ---------- .. [1] T. Andreescu, D. Andrica, "Quadratic Diophantine Equations", Springer-Verlag, New York, 2015. Examples -------- >>> pell_fundamental_solution(61) (1766319049, 226153980) >>> 1766319049**2 - 61*226153980**2 1 >>> pell_fundamental_solution(17, -1) (4, 1) >>> 4**2 - 17*1**2 -1 >>> pell_fundamental_solution(15, -1) Traceback (most recent call last): ... ValueError: pell_fundamental_solution: Solution nonexistent. >>> pell_fundamental_solution(15, -2) Traceback (most recent call last): ... ValueError: pell_fundamental_solution: Must have D > 0 not a perfect square and n in (-1, 1). """ if D <= 0 or is_square(D) or n not in (1, -1): raise ValueError( "pell_fundamental_solution: Must have D > 0 not a perfect square and n in (-1, 1)." ) contfrac = QuadraticIrrational(D) # No solution for n == -1 if the period length is even. if n == -1 and contfrac.period_length % 2 == 0: raise ValueError("pell_fundamental_solution: Solution nonexistent.") # Otherwise, solutions always exist for D not a perfect square. for (x, y) in contfrac.convergents(): if x * x - D * y * y == n: return (x, y)