def test_sqrts_mod_n(self): self.assertEqual(sqrts_mod_n(3, 178137047), [16152263, 28026114, 150110933, 161984784]) self.assertEqual(sqrts_mod_n(49, 53**3*61**4), [7, 721465236980, 1339862033577, 2061327270550]) self.assertEqual(sqrts_mod_n(-1, 5**3*7**2), []) for n in xrange(2, 200): values = defaultdict(list) for a in xrange(n): values[a*a % n].append(a) for a in xrange(n): self.assertEqual(values[a], sqrts_mod_n(a, n))
def _pell_general(D, N, one_solution=False): """ test with D = 1121311213, N = 11889485036288588 """ # Find fundamental solution to Pell for (t, u) in QuadraticIrrational(D).convergents(): if t*t - D*u*u == 1: break # Now find fundamental solution to general Pell fundamental_solutions = [] residues = sqrts_mod_n(N, D) L = N*(t - 1) print residues for r in residues: for x in itertools.count(r, D): w = (x*x - N)//D if w < 0: continue elif 2*D*w > L: break y = integer_sqrt(w) if y*y == w: if one_solution: yield (x, y) return fundamental_solutions.append((x, y)) (r, s) = (-x, y) if (x*r - D*y*s) % N != 0 or (x*s - y*r) % N != 0: fundamental_solutions.append((r, s)) minimal_positive_solutions = [] for (x, y) in fundamental_solutions: if x < 0: (r, s) = (-x, -y) p = r*t + s*u*D q = r*u + s*t minimal_positive_solutions.append((p, q)) else: minimal_positive_solutions.append((x, y)) minimal_positive_solutions.sort() if not minimal_positive_solutions: return print minimal_positive_solutions while True: for i in xrange(len(minimal_positive_solutions)): x, y = minimal_positive_solutions[i] yield (x, y) minimal_positive_solutions[i] = (x*t + y*u*D, x*u + y*t)
def _mollin(D, N): positive_roots = sqrts_mod_n(D, abs(N)) roots = [] print positive_roots for r in positive_roots: for e in [-r, r]: if -abs(N) < 2*e <= abs(N): roots.append(e) return roots
def pell_minimal_positive_solutions(D, N): r"""Returns the minimal positive solutions to the Pell equation x**2 - D*y**2 == N. Given D > 0 not a square, and N != 0, this method returns a list of the minimal positive solutions in each class for the Pell equation described above. Parameters ---------- D : int (D > 0) N : int (N != 0) Returns ------- L : list List of the minimal positive solutions of the equation. The list is empty if there are no solutions. Notes ----- References ---------- .. [1] T. Andreescu, D. Andrica, "Quadratic Diophantine Equations", Springer-Verlag, New York, 2015. .. [2] R.A. Mollin, "Fundamental Number Theory with Applications", Chapman & Hall/CRC, 2008. Examples -------- >>> pell_minimal_positive_solutions(13, 27) [(12, 3), (40, 11), (220, 61), (768, 213)] >>> pell_minimal_positive_solutions( """ (u, v) = pell_fundamental_solution(D, 1) # If N == 1, there is only one class of solutions. if N == 1: return [(u, v)] if N > 0: # See Theorem 7.1 in [2] B = int(((u + 1) * N / 2.0)**(0.5)) + 1 else: # See Theorem 7.2 in [2] B = int(((u - 1) * (-N) / 2.0)**(0.5)) + 1 # We can proceed in two different ways at this point: # 1) We can check all values of y in the range described in Theorems # 7.1 and 7.2, finding the corresponding valid values of x for each. # # 2) By solving a quadratic congruence, we get congruences that x must # satisfy. By iterating through these progressions, we are able to # check the entire range faster than in 1. # # We use the second method here. residues = sqrts_mod_n(N, D) fundamental_solutions = [] # Find the fundamental solutions for each class. for r in residues: for x in xrange(r, B, D): y = is_square((x * x - N) // D) if y > 0 and y is not False: if (-x * x - D * y * y) % N == 0 and 2 * x * y % N == 0: fundamental_solutions.append((x, y)) else: fundamental_solutions.append((x, y)) fundamental_solutions.append((-x, y)) # Find the minimal positive solutions for each class. minimal_positive_solutions = [] for (x, y) in fundamental_solutions: if x < 0: if N > 0: (p, q) = (-x, -y) else: (p, q) = (x, y) p, q = p * u + q * v * D, p * v + q * u else: (p, q) = (x, y) minimal_positive_solutions.append((p, q)) minimal_positive_solutions.sort() return minimal_positive_solutions