Coverage for /usr/lib/python3/dist-packages/sympy/ntheory/residue_ntheory.py: 6%

786 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from __future__ import annotations 

2 

3from sympy.core.function import Function 

4from sympy.core.numbers import igcd, igcdex, mod_inverse 

5from sympy.core.power import isqrt 

6from sympy.core.singleton import S 

7from sympy.polys import Poly 

8from sympy.polys.domains import ZZ 

9from sympy.polys.galoistools import gf_crt1, gf_crt2, linear_congruence 

10from .primetest import isprime 

11from .factor_ import factorint, trailing, totient, multiplicity, perfect_power 

12from sympy.utilities.misc import as_int 

13from sympy.core.random import _randint, randint 

14 

15from itertools import cycle, product 

16 

17 

18def n_order(a, n): 

19 """Returns the order of ``a`` modulo ``n``. 

20 

21 The order of ``a`` modulo ``n`` is the smallest integer 

22 ``k`` such that ``a**k`` leaves a remainder of 1 with ``n``. 

23 

24 Parameters 

25 ========== 

26 

27 a : integer 

28 n : integer, n > 1. a and n should be relatively prime 

29 

30 Examples 

31 ======== 

32 

33 >>> from sympy.ntheory import n_order 

34 >>> n_order(3, 7) 

35 6 

36 >>> n_order(4, 7) 

37 3 

38 """ 

39 from collections import defaultdict 

40 a, n = as_int(a), as_int(n) 

41 if n <= 1: 

42 raise ValueError("n should be an integer greater than 1") 

43 a = a % n 

44 # Trivial 

45 if a == 1: 

46 return 1 

47 if igcd(a, n) != 1: 

48 raise ValueError("The two numbers should be relatively prime") 

49 # We want to calculate 

50 # order = totient(n), factors = factorint(order) 

51 factors = defaultdict(int) 

52 for px, kx in factorint(n).items(): 

53 if kx > 1: 

54 factors[px] += kx - 1 

55 for py, ky in factorint(px - 1).items(): 

56 factors[py] += ky 

57 order = 1 

58 for px, kx in factors.items(): 

59 order *= px**kx 

60 # Now the `order` is the order of the group. 

61 # The order of `a` divides the order of the group. 

62 for p, e in factors.items(): 

63 for _ in range(e): 

64 if pow(a, order // p, n) == 1: 

65 order //= p 

66 else: 

67 break 

68 return order 

69 

70 

71def _primitive_root_prime_iter(p): 

72 """ 

73 Generates the primitive roots for a prime ``p`` 

74 

75 Examples 

76 ======== 

77 

78 >>> from sympy.ntheory.residue_ntheory import _primitive_root_prime_iter 

79 >>> list(_primitive_root_prime_iter(19)) 

80 [2, 3, 10, 13, 14, 15] 

81 

82 References 

83 ========== 

84 

85 .. [1] W. Stein "Elementary Number Theory" (2011), page 44 

86 

87 """ 

88 # it is assumed that p is an int 

89 v = [(p - 1) // i for i in factorint(p - 1).keys()] 

90 a = 2 

91 while a < p: 

92 for pw in v: 

93 # a TypeError below may indicate that p was not an int 

94 if pow(a, pw, p) == 1: 

95 break 

96 else: 

97 yield a 

98 a += 1 

99 

100 

101def primitive_root(p): 

102 """ 

103 Returns the smallest primitive root or None. 

104 

105 Parameters 

106 ========== 

107 

108 p : positive integer 

109 

110 Examples 

111 ======== 

112 

113 >>> from sympy.ntheory.residue_ntheory import primitive_root 

114 >>> primitive_root(19) 

115 2 

116 

117 References 

118 ========== 

119 

120 .. [1] W. Stein "Elementary Number Theory" (2011), page 44 

121 .. [2] P. Hackman "Elementary Number Theory" (2009), Chapter C 

122 

123 """ 

124 p = as_int(p) 

125 if p < 1: 

126 raise ValueError('p is required to be positive') 

127 if p <= 2: 

128 return 1 

129 f = factorint(p) 

130 if len(f) > 2: 

131 return None 

132 if len(f) == 2: 

133 if 2 not in f or f[2] > 1: 

134 return None 

135 

136 # case p = 2*p1**k, p1 prime 

137 for p1, e1 in f.items(): 

138 if p1 != 2: 

139 break 

140 i = 1 

141 while i < p: 

142 i += 2 

143 if i % p1 == 0: 

144 continue 

145 if is_primitive_root(i, p): 

146 return i 

147 

148 else: 

149 if 2 in f: 

150 if p == 4: 

151 return 3 

152 return None 

153 p1, n = list(f.items())[0] 

154 if n > 1: 

155 # see Ref [2], page 81 

156 g = primitive_root(p1) 

157 if is_primitive_root(g, p1**2): 

158 return g 

159 else: 

160 for i in range(2, g + p1 + 1): 

161 if igcd(i, p) == 1 and is_primitive_root(i, p): 

162 return i 

163 

164 return next(_primitive_root_prime_iter(p)) 

165 

166 

167def is_primitive_root(a, p): 

168 """ 

169 Returns True if ``a`` is a primitive root of ``p``. 

170 

171 ``a`` is said to be the primitive root of ``p`` if gcd(a, p) == 1 and 

172 totient(p) is the smallest positive number s.t. 

173 

174 a**totient(p) cong 1 mod(p) 

175 

176 Parameters 

177 ========== 

178 

179 a : integer 

180 p : integer, p > 1. a and p should be relatively prime 

181 

182 Examples 

183 ======== 

184 

185 >>> from sympy.ntheory import is_primitive_root, n_order, totient 

186 >>> is_primitive_root(3, 10) 

187 True 

188 >>> is_primitive_root(9, 10) 

189 False 

190 >>> n_order(3, 10) == totient(10) 

191 True 

192 >>> n_order(9, 10) == totient(10) 

193 False 

194 

195 """ 

196 a, p = as_int(a), as_int(p) 

197 if p <= 1: 

198 raise ValueError("p should be an integer greater than 1") 

199 a = a % p 

200 if igcd(a, p) != 1: 

201 raise ValueError("The two numbers should be relatively prime") 

202 # Primitive root of p exist only for 

203 # p = 2, 4, q**e, 2*q**e (q is odd prime) 

204 if p <= 4: 

205 # The primitive root is only p-1. 

206 return a == p - 1 

207 t = trailing(p) 

208 if t > 1: 

209 return False 

210 q = p >> t 

211 if isprime(q): 

212 group_order = q - 1 

213 factors = set(factorint(q - 1).keys()) 

214 else: 

215 m = perfect_power(q) 

216 if not m: 

217 return False 

218 q, e = m 

219 if not isprime(q): 

220 return False 

221 group_order = q**(e - 1)*(q - 1) 

222 factors = set(factorint(q - 1).keys()) 

223 factors.add(q) 

224 return all(pow(a, group_order // prime, p) != 1 for prime in factors) 

225 

226 

227def _sqrt_mod_tonelli_shanks(a, p): 

228 """ 

229 Returns the square root in the case of ``p`` prime with ``p == 1 (mod 8)`` 

230 

231 References 

232 ========== 

233 

234 .. [1] R. Crandall and C. Pomerance "Prime Numbers", 2nt Ed., page 101 

235 

236 """ 

237 s = trailing(p - 1) 

238 t = p >> s 

239 # find a non-quadratic residue 

240 while 1: 

241 d = randint(2, p - 1) 

242 r = legendre_symbol(d, p) 

243 if r == -1: 

244 break 

245 #assert legendre_symbol(d, p) == -1 

246 A = pow(a, t, p) 

247 D = pow(d, t, p) 

248 m = 0 

249 for i in range(s): 

250 adm = A*pow(D, m, p) % p 

251 adm = pow(adm, 2**(s - 1 - i), p) 

252 if adm % p == p - 1: 

253 m += 2**i 

254 #assert A*pow(D, m, p) % p == 1 

255 x = pow(a, (t + 1)//2, p)*pow(D, m//2, p) % p 

256 return x 

257 

258 

259def sqrt_mod(a, p, all_roots=False): 

260 """ 

261 Find a root of ``x**2 = a mod p``. 

262 

263 Parameters 

264 ========== 

265 

266 a : integer 

267 p : positive integer 

268 all_roots : if True the list of roots is returned or None 

269 

270 Notes 

271 ===== 

272 

273 If there is no root it is returned None; else the returned root 

274 is less or equal to ``p // 2``; in general is not the smallest one. 

275 It is returned ``p // 2`` only if it is the only root. 

276 

277 Use ``all_roots`` only when it is expected that all the roots fit 

278 in memory; otherwise use ``sqrt_mod_iter``. 

279 

280 Examples 

281 ======== 

282 

283 >>> from sympy.ntheory import sqrt_mod 

284 >>> sqrt_mod(11, 43) 

285 21 

286 >>> sqrt_mod(17, 32, True) 

287 [7, 9, 23, 25] 

288 """ 

289 if all_roots: 

290 return sorted(sqrt_mod_iter(a, p)) 

291 try: 

292 p = abs(as_int(p)) 

293 it = sqrt_mod_iter(a, p) 

294 r = next(it) 

295 if r > p // 2: 

296 return p - r 

297 elif r < p // 2: 

298 return r 

299 else: 

300 try: 

301 r = next(it) 

302 if r > p // 2: 

303 return p - r 

304 except StopIteration: 

305 pass 

306 return r 

307 except StopIteration: 

308 return None 

309 

310 

311def _product(*iters): 

312 """ 

313 Cartesian product generator 

314 

315 Notes 

316 ===== 

317 

318 Unlike itertools.product, it works also with iterables which do not fit 

319 in memory. See https://bugs.python.org/issue10109 

320 

321 Author: Fernando Sumudu 

322 with small changes 

323 """ 

324 inf_iters = tuple(cycle(enumerate(it)) for it in iters) 

325 num_iters = len(inf_iters) 

326 cur_val = [None]*num_iters 

327 

328 first_v = True 

329 while True: 

330 i, p = 0, num_iters 

331 while p and not i: 

332 p -= 1 

333 i, cur_val[p] = next(inf_iters[p]) 

334 

335 if not p and not i: 

336 if first_v: 

337 first_v = False 

338 else: 

339 break 

340 

341 yield cur_val 

342 

343 

344def sqrt_mod_iter(a, p, domain=int): 

345 """ 

346 Iterate over solutions to ``x**2 = a mod p``. 

347 

348 Parameters 

349 ========== 

350 

351 a : integer 

352 p : positive integer 

353 domain : integer domain, ``int``, ``ZZ`` or ``Integer`` 

354 

355 Examples 

356 ======== 

357 

358 >>> from sympy.ntheory.residue_ntheory import sqrt_mod_iter 

359 >>> list(sqrt_mod_iter(11, 43)) 

360 [21, 22] 

361 """ 

362 a, p = as_int(a), abs(as_int(p)) 

363 if isprime(p): 

364 a = a % p 

365 if a == 0: 

366 res = _sqrt_mod1(a, p, 1) 

367 else: 

368 res = _sqrt_mod_prime_power(a, p, 1) 

369 if res: 

370 if domain is ZZ: 

371 yield from res 

372 else: 

373 for x in res: 

374 yield domain(x) 

375 else: 

376 f = factorint(p) 

377 v = [] 

378 pv = [] 

379 for px, ex in f.items(): 

380 if a % px == 0: 

381 rx = _sqrt_mod1(a, px, ex) 

382 if not rx: 

383 return 

384 else: 

385 rx = _sqrt_mod_prime_power(a, px, ex) 

386 if not rx: 

387 return 

388 v.append(rx) 

389 pv.append(px**ex) 

390 mm, e, s = gf_crt1(pv, ZZ) 

391 if domain is ZZ: 

392 for vx in _product(*v): 

393 r = gf_crt2(vx, pv, mm, e, s, ZZ) 

394 yield r 

395 else: 

396 for vx in _product(*v): 

397 r = gf_crt2(vx, pv, mm, e, s, ZZ) 

398 yield domain(r) 

399 

400 

401def _sqrt_mod_prime_power(a, p, k): 

402 """ 

403 Find the solutions to ``x**2 = a mod p**k`` when ``a % p != 0`` 

404 

405 Parameters 

406 ========== 

407 

408 a : integer 

409 p : prime number 

410 k : positive integer 

411 

412 Examples 

413 ======== 

414 

415 >>> from sympy.ntheory.residue_ntheory import _sqrt_mod_prime_power 

416 >>> _sqrt_mod_prime_power(11, 43, 1) 

417 [21, 22] 

418 

419 References 

420 ========== 

421 

422 .. [1] P. Hackman "Elementary Number Theory" (2009), page 160 

423 .. [2] http://www.numbertheory.org/php/squareroot.html 

424 .. [3] [Gathen99]_ 

425 """ 

426 pk = p**k 

427 a = a % pk 

428 

429 if k == 1: 

430 if p == 2: 

431 return [ZZ(a)] 

432 if not (a % p < 2 or pow(a, (p - 1) // 2, p) == 1): 

433 return None 

434 

435 if p % 4 == 3: 

436 res = pow(a, (p + 1) // 4, p) 

437 elif p % 8 == 5: 

438 sign = pow(a, (p - 1) // 4, p) 

439 if sign == 1: 

440 res = pow(a, (p + 3) // 8, p) 

441 else: 

442 b = pow(4*a, (p - 5) // 8, p) 

443 x = (2*a*b) % p 

444 if pow(x, 2, p) == a: 

445 res = x 

446 else: 

447 res = _sqrt_mod_tonelli_shanks(a, p) 

448 

449 # ``_sqrt_mod_tonelli_shanks(a, p)`` is not deterministic; 

450 # sort to get always the same result 

451 return sorted([ZZ(res), ZZ(p - res)]) 

452 

453 if k > 1: 

454 # see Ref.[2] 

455 if p == 2: 

456 if a % 8 != 1: 

457 return None 

458 if k <= 3: 

459 s = set() 

460 for i in range(0, pk, 4): 

461 s.add(1 + i) 

462 s.add(-1 + i) 

463 return list(s) 

464 # according to Ref.[2] for k > 2 there are two solutions 

465 # (mod 2**k-1), that is four solutions (mod 2**k), which can be 

466 # obtained from the roots of x**2 = 0 (mod 8) 

467 rv = [ZZ(1), ZZ(3), ZZ(5), ZZ(7)] 

468 # hensel lift them to solutions of x**2 = 0 (mod 2**k) 

469 # if r**2 - a = 0 mod 2**nx but not mod 2**(nx+1) 

470 # then r + 2**(nx - 1) is a root mod 2**(nx+1) 

471 n = 3 

472 res = [] 

473 for r in rv: 

474 nx = n 

475 while nx < k: 

476 r1 = (r**2 - a) >> nx 

477 if r1 % 2: 

478 r = r + (1 << (nx - 1)) 

479 #assert (r**2 - a)% (1 << (nx + 1)) == 0 

480 nx += 1 

481 if r not in res: 

482 res.append(r) 

483 x = r + (1 << (k - 1)) 

484 #assert (x**2 - a) % pk == 0 

485 if x < (1 << nx) and x not in res: 

486 if (x**2 - a) % pk == 0: 

487 res.append(x) 

488 return res 

489 rv = _sqrt_mod_prime_power(a, p, 1) 

490 if not rv: 

491 return None 

492 r = rv[0] 

493 fr = r**2 - a 

494 # hensel lifting with Newton iteration, see Ref.[3] chapter 9 

495 # with f(x) = x**2 - a; one has f'(a) != 0 (mod p) for p != 2 

496 n = 1 

497 px = p 

498 while 1: 

499 n1 = n 

500 n1 *= 2 

501 if n1 > k: 

502 break 

503 n = n1 

504 px = px**2 

505 frinv = igcdex(2*r, px)[0] 

506 r = (r - fr*frinv) % px 

507 fr = r**2 - a 

508 if n < k: 

509 px = p**k 

510 frinv = igcdex(2*r, px)[0] 

511 r = (r - fr*frinv) % px 

512 return [r, px - r] 

513 

514 

515def _sqrt_mod1(a, p, n): 

516 """ 

517 Find solution to ``x**2 == a mod p**n`` when ``a % p == 0`` 

518 

519 see http://www.numbertheory.org/php/squareroot.html 

520 """ 

521 pn = p**n 

522 a = a % pn 

523 if a == 0: 

524 # case gcd(a, p**k) = p**n 

525 m = n // 2 

526 if n % 2 == 1: 

527 pm1 = p**(m + 1) 

528 def _iter0a(): 

529 i = 0 

530 while i < pn: 

531 yield i 

532 i += pm1 

533 return _iter0a() 

534 else: 

535 pm = p**m 

536 def _iter0b(): 

537 i = 0 

538 while i < pn: 

539 yield i 

540 i += pm 

541 return _iter0b() 

542 

543 # case gcd(a, p**k) = p**r, r < n 

544 f = factorint(a) 

545 r = f[p] 

546 if r % 2 == 1: 

547 return None 

548 m = r // 2 

549 a1 = a >> r 

550 if p == 2: 

551 if n - r == 1: 

552 pnm1 = 1 << (n - m + 1) 

553 pm1 = 1 << (m + 1) 

554 def _iter1(): 

555 k = 1 << (m + 2) 

556 i = 1 << m 

557 while i < pnm1: 

558 j = i 

559 while j < pn: 

560 yield j 

561 j += k 

562 i += pm1 

563 return _iter1() 

564 if n - r == 2: 

565 res = _sqrt_mod_prime_power(a1, p, n - r) 

566 if res is None: 

567 return None 

568 pnm = 1 << (n - m) 

569 def _iter2(): 

570 s = set() 

571 for r in res: 

572 i = 0 

573 while i < pn: 

574 x = (r << m) + i 

575 if x not in s: 

576 s.add(x) 

577 yield x 

578 i += pnm 

579 return _iter2() 

580 if n - r > 2: 

581 res = _sqrt_mod_prime_power(a1, p, n - r) 

582 if res is None: 

583 return None 

584 pnm1 = 1 << (n - m - 1) 

585 def _iter3(): 

586 s = set() 

587 for r in res: 

588 i = 0 

589 while i < pn: 

590 x = ((r << m) + i) % pn 

591 if x not in s: 

592 s.add(x) 

593 yield x 

594 i += pnm1 

595 return _iter3() 

596 else: 

597 m = r // 2 

598 a1 = a // p**r 

599 res1 = _sqrt_mod_prime_power(a1, p, n - r) 

600 if res1 is None: 

601 return None 

602 pm = p**m 

603 pnr = p**(n-r) 

604 pnm = p**(n-m) 

605 

606 def _iter4(): 

607 s = set() 

608 pm = p**m 

609 for rx in res1: 

610 i = 0 

611 while i < pnm: 

612 x = ((rx + i) % pn) 

613 if x not in s: 

614 s.add(x) 

615 yield x*pm 

616 i += pnr 

617 return _iter4() 

618 

619 

620def is_quad_residue(a, p): 

621 """ 

622 Returns True if ``a`` (mod ``p``) is in the set of squares mod ``p``, 

623 i.e a % p in set([i**2 % p for i in range(p)]). 

624 

625 Examples 

626 ======== 

627 

628 If ``p`` is an odd 

629 prime, an iterative method is used to make the determination: 

630 

631 >>> from sympy.ntheory import is_quad_residue 

632 >>> sorted(set([i**2 % 7 for i in range(7)])) 

633 [0, 1, 2, 4] 

634 >>> [j for j in range(7) if is_quad_residue(j, 7)] 

635 [0, 1, 2, 4] 

636 

637 See Also 

638 ======== 

639 

640 legendre_symbol, jacobi_symbol 

641 """ 

642 a, p = as_int(a), as_int(p) 

643 if p < 1: 

644 raise ValueError('p must be > 0') 

645 if a >= p or a < 0: 

646 a = a % p 

647 if a < 2 or p < 3: 

648 return True 

649 if not isprime(p): 

650 if p % 2 and jacobi_symbol(a, p) == -1: 

651 return False 

652 r = sqrt_mod(a, p) 

653 if r is None: 

654 return False 

655 else: 

656 return True 

657 

658 return pow(a, (p - 1) // 2, p) == 1 

659 

660 

661def is_nthpow_residue(a, n, m): 

662 """ 

663 Returns True if ``x**n == a (mod m)`` has solutions. 

664 

665 References 

666 ========== 

667 

668 .. [1] P. Hackman "Elementary Number Theory" (2009), page 76 

669 

670 """ 

671 a = a % m 

672 a, n, m = as_int(a), as_int(n), as_int(m) 

673 if m <= 0: 

674 raise ValueError('m must be > 0') 

675 if n < 0: 

676 raise ValueError('n must be >= 0') 

677 if n == 0: 

678 if m == 1: 

679 return False 

680 return a == 1 

681 if a == 0: 

682 return True 

683 if n == 1: 

684 return True 

685 if n == 2: 

686 return is_quad_residue(a, m) 

687 return _is_nthpow_residue_bign(a, n, m) 

688 

689 

690def _is_nthpow_residue_bign(a, n, m): 

691 r"""Returns True if `x^n = a \pmod{n}` has solutions for `n > 2`.""" 

692 # assert n > 2 

693 # assert a > 0 and m > 0 

694 if primitive_root(m) is None or igcd(a, m) != 1: 

695 # assert m >= 8 

696 for prime, power in factorint(m).items(): 

697 if not _is_nthpow_residue_bign_prime_power(a, n, prime, power): 

698 return False 

699 return True 

700 f = totient(m) 

701 k = int(f // igcd(f, n)) 

702 return pow(a, k, int(m)) == 1 

703 

704 

705def _is_nthpow_residue_bign_prime_power(a, n, p, k): 

706 r"""Returns True/False if a solution for `x^n = a \pmod{p^k}` 

707 does/does not exist.""" 

708 # assert a > 0 

709 # assert n > 2 

710 # assert p is prime 

711 # assert k > 0 

712 if a % p: 

713 if p != 2: 

714 return _is_nthpow_residue_bign(a, n, pow(p, k)) 

715 if n & 1: 

716 return True 

717 c = trailing(n) 

718 return a % pow(2, min(c + 2, k)) == 1 

719 else: 

720 a %= pow(p, k) 

721 if not a: 

722 return True 

723 mu = multiplicity(p, a) 

724 if mu % n: 

725 return False 

726 pm = pow(p, mu) 

727 return _is_nthpow_residue_bign_prime_power(a//pm, n, p, k - mu) 

728 

729 

730def _nthroot_mod2(s, q, p): 

731 f = factorint(q) 

732 v = [] 

733 for b, e in f.items(): 

734 v.extend([b]*e) 

735 for qx in v: 

736 s = _nthroot_mod1(s, qx, p, False) 

737 return s 

738 

739 

740def _nthroot_mod1(s, q, p, all_roots): 

741 """ 

742 Root of ``x**q = s mod p``, ``p`` prime and ``q`` divides ``p - 1`` 

743 

744 References 

745 ========== 

746 

747 .. [1] A. M. Johnston "A Generalized qth Root Algorithm" 

748 

749 """ 

750 g = primitive_root(p) 

751 if not isprime(q): 

752 r = _nthroot_mod2(s, q, p) 

753 else: 

754 f = p - 1 

755 assert (p - 1) % q == 0 

756 # determine k 

757 k = 0 

758 while f % q == 0: 

759 k += 1 

760 f = f // q 

761 # find z, x, r1 

762 f1 = igcdex(-f, q)[0] % q 

763 z = f*f1 

764 x = (1 + z) // q 

765 r1 = pow(s, x, p) 

766 s1 = pow(s, f, p) 

767 h = pow(g, f*q, p) 

768 t = discrete_log(p, s1, h) 

769 g2 = pow(g, z*t, p) 

770 g3 = igcdex(g2, p)[0] 

771 r = r1*g3 % p 

772 #assert pow(r, q, p) == s 

773 res = [r] 

774 h = pow(g, (p - 1) // q, p) 

775 #assert pow(h, q, p) == 1 

776 hx = r 

777 for i in range(q - 1): 

778 hx = (hx*h) % p 

779 res.append(hx) 

780 if all_roots: 

781 res.sort() 

782 return res 

783 return min(res) 

784 

785 

786 

787def _help(m, prime_modulo_method, diff_method, expr_val): 

788 """ 

789 Helper function for _nthroot_mod_composite and polynomial_congruence. 

790 

791 Parameters 

792 ========== 

793 

794 m : positive integer 

795 prime_modulo_method : function to calculate the root of the congruence 

796 equation for the prime divisors of m 

797 diff_method : function to calculate derivative of expression at any 

798 given point 

799 expr_val : function to calculate value of the expression at any 

800 given point 

801 """ 

802 from sympy.ntheory.modular import crt 

803 f = factorint(m) 

804 dd = {} 

805 for p, e in f.items(): 

806 tot_roots = set() 

807 if e == 1: 

808 tot_roots.update(prime_modulo_method(p)) 

809 else: 

810 for root in prime_modulo_method(p): 

811 diff = diff_method(root, p) 

812 if diff != 0: 

813 ppow = p 

814 m_inv = mod_inverse(diff, p) 

815 for j in range(1, e): 

816 ppow *= p 

817 root = (root - expr_val(root, ppow) * m_inv) % ppow 

818 tot_roots.add(root) 

819 else: 

820 new_base = p 

821 roots_in_base = {root} 

822 while new_base < pow(p, e): 

823 new_base *= p 

824 new_roots = set() 

825 for k in roots_in_base: 

826 if expr_val(k, new_base)!= 0: 

827 continue 

828 while k not in new_roots: 

829 new_roots.add(k) 

830 k = (k + (new_base // p)) % new_base 

831 roots_in_base = new_roots 

832 tot_roots = tot_roots | roots_in_base 

833 if tot_roots == set(): 

834 return [] 

835 dd[pow(p, e)] = tot_roots 

836 a = [] 

837 m = [] 

838 for x, y in dd.items(): 

839 m.append(x) 

840 a.append(list(y)) 

841 return sorted({crt(m, list(i))[0] for i in product(*a)}) 

842 

843 

844def _nthroot_mod_composite(a, n, m): 

845 """ 

846 Find the solutions to ``x**n = a mod m`` when m is not prime. 

847 """ 

848 return _help(m, 

849 lambda p: nthroot_mod(a, n, p, True), 

850 lambda root, p: (pow(root, n - 1, p) * (n % p)) % p, 

851 lambda root, p: (pow(root, n, p) - a) % p) 

852 

853 

854def nthroot_mod(a, n, p, all_roots=False): 

855 """ 

856 Find the solutions to ``x**n = a mod p``. 

857 

858 Parameters 

859 ========== 

860 

861 a : integer 

862 n : positive integer 

863 p : positive integer 

864 all_roots : if False returns the smallest root, else the list of roots 

865 

866 Examples 

867 ======== 

868 

869 >>> from sympy.ntheory.residue_ntheory import nthroot_mod 

870 >>> nthroot_mod(11, 4, 19) 

871 8 

872 >>> nthroot_mod(11, 4, 19, True) 

873 [8, 11] 

874 >>> nthroot_mod(68, 3, 109) 

875 23 

876 """ 

877 a = a % p 

878 a, n, p = as_int(a), as_int(n), as_int(p) 

879 

880 if n == 2: 

881 return sqrt_mod(a, p, all_roots) 

882 # see Hackman "Elementary Number Theory" (2009), page 76 

883 if not isprime(p): 

884 return _nthroot_mod_composite(a, n, p) 

885 if a % p == 0: 

886 return [0] 

887 if not is_nthpow_residue(a, n, p): 

888 return [] if all_roots else None 

889 if (p - 1) % n == 0: 

890 return _nthroot_mod1(a, n, p, all_roots) 

891 # The roots of ``x**n - a = 0 (mod p)`` are roots of 

892 # ``gcd(x**n - a, x**(p - 1) - 1) = 0 (mod p)`` 

893 pa = n 

894 pb = p - 1 

895 b = 1 

896 if pa < pb: 

897 a, pa, b, pb = b, pb, a, pa 

898 while pb: 

899 # x**pa - a = 0; x**pb - b = 0 

900 # x**pa - a = x**(q*pb + r) - a = (x**pb)**q * x**r - a = 

901 # b**q * x**r - a; x**r - c = 0; c = b**-q * a mod p 

902 q, r = divmod(pa, pb) 

903 c = pow(b, q, p) 

904 c = igcdex(c, p)[0] 

905 c = (c * a) % p 

906 pa, pb = pb, r 

907 a, b = b, c 

908 if pa == 1: 

909 if all_roots: 

910 res = [a] 

911 else: 

912 res = a 

913 elif pa == 2: 

914 return sqrt_mod(a, p, all_roots) 

915 else: 

916 res = _nthroot_mod1(a, pa, p, all_roots) 

917 return res 

918 

919 

920def quadratic_residues(p) -> list[int]: 

921 """ 

922 Returns the list of quadratic residues. 

923 

924 Examples 

925 ======== 

926 

927 >>> from sympy.ntheory.residue_ntheory import quadratic_residues 

928 >>> quadratic_residues(7) 

929 [0, 1, 2, 4] 

930 """ 

931 p = as_int(p) 

932 r = {pow(i, 2, p) for i in range(p // 2 + 1)} 

933 return sorted(r) 

934 

935 

936def legendre_symbol(a, p): 

937 r""" 

938 Returns the Legendre symbol `(a / p)`. 

939 

940 For an integer ``a`` and an odd prime ``p``, the Legendre symbol is 

941 defined as 

942 

943 .. math :: 

944 \genfrac(){}{}{a}{p} = \begin{cases} 

945 0 & \text{if } p \text{ divides } a\\ 

946 1 & \text{if } a \text{ is a quadratic residue modulo } p\\ 

947 -1 & \text{if } a \text{ is a quadratic nonresidue modulo } p 

948 \end{cases} 

949 

950 Parameters 

951 ========== 

952 

953 a : integer 

954 p : odd prime 

955 

956 Examples 

957 ======== 

958 

959 >>> from sympy.ntheory import legendre_symbol 

960 >>> [legendre_symbol(i, 7) for i in range(7)] 

961 [0, 1, 1, -1, 1, -1, -1] 

962 >>> sorted(set([i**2 % 7 for i in range(7)])) 

963 [0, 1, 2, 4] 

964 

965 See Also 

966 ======== 

967 

968 is_quad_residue, jacobi_symbol 

969 

970 """ 

971 a, p = as_int(a), as_int(p) 

972 if not isprime(p) or p == 2: 

973 raise ValueError("p should be an odd prime") 

974 a = a % p 

975 if not a: 

976 return 0 

977 if pow(a, (p - 1) // 2, p) == 1: 

978 return 1 

979 return -1 

980 

981 

982def jacobi_symbol(m, n): 

983 r""" 

984 Returns the Jacobi symbol `(m / n)`. 

985 

986 For any integer ``m`` and any positive odd integer ``n`` the Jacobi symbol 

987 is defined as the product of the Legendre symbols corresponding to the 

988 prime factors of ``n``: 

989 

990 .. math :: 

991 \genfrac(){}{}{m}{n} = 

992 \genfrac(){}{}{m}{p^{1}}^{\alpha_1} 

993 \genfrac(){}{}{m}{p^{2}}^{\alpha_2} 

994 ... 

995 \genfrac(){}{}{m}{p^{k}}^{\alpha_k} 

996 \text{ where } n = 

997 p_1^{\alpha_1} 

998 p_2^{\alpha_2} 

999 ... 

1000 p_k^{\alpha_k} 

1001 

1002 Like the Legendre symbol, if the Jacobi symbol `\genfrac(){}{}{m}{n} = -1` 

1003 then ``m`` is a quadratic nonresidue modulo ``n``. 

1004 

1005 But, unlike the Legendre symbol, if the Jacobi symbol 

1006 `\genfrac(){}{}{m}{n} = 1` then ``m`` may or may not be a quadratic residue 

1007 modulo ``n``. 

1008 

1009 Parameters 

1010 ========== 

1011 

1012 m : integer 

1013 n : odd positive integer 

1014 

1015 Examples 

1016 ======== 

1017 

1018 >>> from sympy.ntheory import jacobi_symbol, legendre_symbol 

1019 >>> from sympy import S 

1020 >>> jacobi_symbol(45, 77) 

1021 -1 

1022 >>> jacobi_symbol(60, 121) 

1023 1 

1024 

1025 The relationship between the ``jacobi_symbol`` and ``legendre_symbol`` can 

1026 be demonstrated as follows: 

1027 

1028 >>> L = legendre_symbol 

1029 >>> S(45).factors() 

1030 {3: 2, 5: 1} 

1031 >>> jacobi_symbol(7, 45) == L(7, 3)**2 * L(7, 5)**1 

1032 True 

1033 

1034 See Also 

1035 ======== 

1036 

1037 is_quad_residue, legendre_symbol 

1038 """ 

1039 m, n = as_int(m), as_int(n) 

1040 if n < 0 or not n % 2: 

1041 raise ValueError("n should be an odd positive integer") 

1042 if m < 0 or m > n: 

1043 m %= n 

1044 if not m: 

1045 return int(n == 1) 

1046 if n == 1 or m == 1: 

1047 return 1 

1048 if igcd(m, n) != 1: 

1049 return 0 

1050 

1051 j = 1 

1052 while m != 0: 

1053 while m % 2 == 0 and m > 0: 

1054 m >>= 1 

1055 if n % 8 in [3, 5]: 

1056 j = -j 

1057 m, n = n, m 

1058 if m % 4 == n % 4 == 3: 

1059 j = -j 

1060 m %= n 

1061 return j 

1062 

1063 

1064class mobius(Function): 

1065 """ 

1066 Mobius function maps natural number to {-1, 0, 1} 

1067 

1068 It is defined as follows: 

1069 1) `1` if `n = 1`. 

1070 2) `0` if `n` has a squared prime factor. 

1071 3) `(-1)^k` if `n` is a square-free positive integer with `k` 

1072 number of prime factors. 

1073 

1074 It is an important multiplicative function in number theory 

1075 and combinatorics. It has applications in mathematical series, 

1076 algebraic number theory and also physics (Fermion operator has very 

1077 concrete realization with Mobius Function model). 

1078 

1079 Parameters 

1080 ========== 

1081 

1082 n : positive integer 

1083 

1084 Examples 

1085 ======== 

1086 

1087 >>> from sympy.ntheory import mobius 

1088 >>> mobius(13*7) 

1089 1 

1090 >>> mobius(1) 

1091 1 

1092 >>> mobius(13*7*5) 

1093 -1 

1094 >>> mobius(13**2) 

1095 0 

1096 

1097 References 

1098 ========== 

1099 

1100 .. [1] https://en.wikipedia.org/wiki/M%C3%B6bius_function 

1101 .. [2] Thomas Koshy "Elementary Number Theory with Applications" 

1102 

1103 """ 

1104 @classmethod 

1105 def eval(cls, n): 

1106 if n.is_integer: 

1107 if n.is_positive is not True: 

1108 raise ValueError("n should be a positive integer") 

1109 else: 

1110 raise TypeError("n should be an integer") 

1111 if n.is_prime: 

1112 return S.NegativeOne 

1113 elif n is S.One: 

1114 return S.One 

1115 elif n.is_Integer: 

1116 a = factorint(n) 

1117 if any(i > 1 for i in a.values()): 

1118 return S.Zero 

1119 return S.NegativeOne**len(a) 

1120 

1121 

1122def _discrete_log_trial_mul(n, a, b, order=None): 

1123 """ 

1124 Trial multiplication algorithm for computing the discrete logarithm of 

1125 ``a`` to the base ``b`` modulo ``n``. 

1126 

1127 The algorithm finds the discrete logarithm using exhaustive search. This 

1128 naive method is used as fallback algorithm of ``discrete_log`` when the 

1129 group order is very small. 

1130 

1131 Examples 

1132 ======== 

1133 

1134 >>> from sympy.ntheory.residue_ntheory import _discrete_log_trial_mul 

1135 >>> _discrete_log_trial_mul(41, 15, 7) 

1136 3 

1137 

1138 See Also 

1139 ======== 

1140 

1141 discrete_log 

1142 

1143 References 

1144 ========== 

1145 

1146 .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & 

1147 Vanstone, S. A. (1997). 

1148 """ 

1149 a %= n 

1150 b %= n 

1151 if order is None: 

1152 order = n 

1153 x = 1 

1154 for i in range(order): 

1155 if x == a: 

1156 return i 

1157 x = x * b % n 

1158 raise ValueError("Log does not exist") 

1159 

1160 

1161def _discrete_log_shanks_steps(n, a, b, order=None): 

1162 """ 

1163 Baby-step giant-step algorithm for computing the discrete logarithm of 

1164 ``a`` to the base ``b`` modulo ``n``. 

1165 

1166 The algorithm is a time-memory trade-off of the method of exhaustive 

1167 search. It uses `O(sqrt(m))` memory, where `m` is the group order. 

1168 

1169 Examples 

1170 ======== 

1171 

1172 >>> from sympy.ntheory.residue_ntheory import _discrete_log_shanks_steps 

1173 >>> _discrete_log_shanks_steps(41, 15, 7) 

1174 3 

1175 

1176 See Also 

1177 ======== 

1178 

1179 discrete_log 

1180 

1181 References 

1182 ========== 

1183 

1184 .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & 

1185 Vanstone, S. A. (1997). 

1186 """ 

1187 a %= n 

1188 b %= n 

1189 if order is None: 

1190 order = n_order(b, n) 

1191 m = isqrt(order) + 1 

1192 T = {} 

1193 x = 1 

1194 for i in range(m): 

1195 T[x] = i 

1196 x = x * b % n 

1197 z = mod_inverse(b, n) 

1198 z = pow(z, m, n) 

1199 x = a 

1200 for i in range(m): 

1201 if x in T: 

1202 return i * m + T[x] 

1203 x = x * z % n 

1204 raise ValueError("Log does not exist") 

1205 

1206 

1207def _discrete_log_pollard_rho(n, a, b, order=None, retries=10, rseed=None): 

1208 """ 

1209 Pollard's Rho algorithm for computing the discrete logarithm of ``a`` to 

1210 the base ``b`` modulo ``n``. 

1211 

1212 It is a randomized algorithm with the same expected running time as 

1213 ``_discrete_log_shanks_steps``, but requires a negligible amount of memory. 

1214 

1215 Examples 

1216 ======== 

1217 

1218 >>> from sympy.ntheory.residue_ntheory import _discrete_log_pollard_rho 

1219 >>> _discrete_log_pollard_rho(227, 3**7, 3) 

1220 7 

1221 

1222 See Also 

1223 ======== 

1224 

1225 discrete_log 

1226 

1227 References 

1228 ========== 

1229 

1230 .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & 

1231 Vanstone, S. A. (1997). 

1232 """ 

1233 a %= n 

1234 b %= n 

1235 

1236 if order is None: 

1237 order = n_order(b, n) 

1238 randint = _randint(rseed) 

1239 

1240 for i in range(retries): 

1241 aa = randint(1, order - 1) 

1242 ba = randint(1, order - 1) 

1243 xa = pow(b, aa, n) * pow(a, ba, n) % n 

1244 

1245 c = xa % 3 

1246 if c == 0: 

1247 xb = a * xa % n 

1248 ab = aa 

1249 bb = (ba + 1) % order 

1250 elif c == 1: 

1251 xb = xa * xa % n 

1252 ab = (aa + aa) % order 

1253 bb = (ba + ba) % order 

1254 else: 

1255 xb = b * xa % n 

1256 ab = (aa + 1) % order 

1257 bb = ba 

1258 

1259 for j in range(order): 

1260 c = xa % 3 

1261 if c == 0: 

1262 xa = a * xa % n 

1263 ba = (ba + 1) % order 

1264 elif c == 1: 

1265 xa = xa * xa % n 

1266 aa = (aa + aa) % order 

1267 ba = (ba + ba) % order 

1268 else: 

1269 xa = b * xa % n 

1270 aa = (aa + 1) % order 

1271 

1272 c = xb % 3 

1273 if c == 0: 

1274 xb = a * xb % n 

1275 bb = (bb + 1) % order 

1276 elif c == 1: 

1277 xb = xb * xb % n 

1278 ab = (ab + ab) % order 

1279 bb = (bb + bb) % order 

1280 else: 

1281 xb = b * xb % n 

1282 ab = (ab + 1) % order 

1283 

1284 c = xb % 3 

1285 if c == 0: 

1286 xb = a * xb % n 

1287 bb = (bb + 1) % order 

1288 elif c == 1: 

1289 xb = xb * xb % n 

1290 ab = (ab + ab) % order 

1291 bb = (bb + bb) % order 

1292 else: 

1293 xb = b * xb % n 

1294 ab = (ab + 1) % order 

1295 

1296 if xa == xb: 

1297 r = (ba - bb) % order 

1298 try: 

1299 e = mod_inverse(r, order) * (ab - aa) % order 

1300 if (pow(b, e, n) - a) % n == 0: 

1301 return e 

1302 except ValueError: 

1303 pass 

1304 break 

1305 raise ValueError("Pollard's Rho failed to find logarithm") 

1306 

1307 

1308def _discrete_log_pohlig_hellman(n, a, b, order=None): 

1309 """ 

1310 Pohlig-Hellman algorithm for computing the discrete logarithm of ``a`` to 

1311 the base ``b`` modulo ``n``. 

1312 

1313 In order to compute the discrete logarithm, the algorithm takes advantage 

1314 of the factorization of the group order. It is more efficient when the 

1315 group order factors into many small primes. 

1316 

1317 Examples 

1318 ======== 

1319 

1320 >>> from sympy.ntheory.residue_ntheory import _discrete_log_pohlig_hellman 

1321 >>> _discrete_log_pohlig_hellman(251, 210, 71) 

1322 197 

1323 

1324 See Also 

1325 ======== 

1326 

1327 discrete_log 

1328 

1329 References 

1330 ========== 

1331 

1332 .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & 

1333 Vanstone, S. A. (1997). 

1334 """ 

1335 from .modular import crt 

1336 a %= n 

1337 b %= n 

1338 

1339 if order is None: 

1340 order = n_order(b, n) 

1341 

1342 f = factorint(order) 

1343 l = [0] * len(f) 

1344 

1345 for i, (pi, ri) in enumerate(f.items()): 

1346 for j in range(ri): 

1347 gj = pow(b, l[i], n) 

1348 aj = pow(a * mod_inverse(gj, n), order // pi**(j + 1), n) 

1349 bj = pow(b, order // pi, n) 

1350 cj = discrete_log(n, aj, bj, pi, True) 

1351 l[i] += cj * pi**j 

1352 

1353 d, _ = crt([pi**ri for pi, ri in f.items()], l) 

1354 return d 

1355 

1356 

1357def discrete_log(n, a, b, order=None, prime_order=None): 

1358 """ 

1359 Compute the discrete logarithm of ``a`` to the base ``b`` modulo ``n``. 

1360 

1361 This is a recursive function to reduce the discrete logarithm problem in 

1362 cyclic groups of composite order to the problem in cyclic groups of prime 

1363 order. 

1364 

1365 It employs different algorithms depending on the problem (subgroup order 

1366 size, prime order or not): 

1367 

1368 * Trial multiplication 

1369 * Baby-step giant-step 

1370 * Pollard's Rho 

1371 * Pohlig-Hellman 

1372 

1373 Examples 

1374 ======== 

1375 

1376 >>> from sympy.ntheory import discrete_log 

1377 >>> discrete_log(41, 15, 7) 

1378 3 

1379 

1380 References 

1381 ========== 

1382 

1383 .. [1] https://mathworld.wolfram.com/DiscreteLogarithm.html 

1384 .. [2] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & 

1385 Vanstone, S. A. (1997). 

1386 

1387 """ 

1388 n, a, b = as_int(n), as_int(a), as_int(b) 

1389 if order is None: 

1390 order = n_order(b, n) 

1391 

1392 if prime_order is None: 

1393 prime_order = isprime(order) 

1394 

1395 if order < 1000: 

1396 return _discrete_log_trial_mul(n, a, b, order) 

1397 elif prime_order: 

1398 if order < 1000000000000: 

1399 return _discrete_log_shanks_steps(n, a, b, order) 

1400 return _discrete_log_pollard_rho(n, a, b, order) 

1401 

1402 return _discrete_log_pohlig_hellman(n, a, b, order) 

1403 

1404 

1405 

1406def quadratic_congruence(a, b, c, p): 

1407 """ 

1408 Find the solutions to ``a x**2 + b x + c = 0 mod p. 

1409 

1410 Parameters 

1411 ========== 

1412 

1413 a : int 

1414 b : int 

1415 c : int 

1416 p : int 

1417 A positive integer. 

1418 """ 

1419 a = as_int(a) 

1420 b = as_int(b) 

1421 c = as_int(c) 

1422 p = as_int(p) 

1423 a = a % p 

1424 b = b % p 

1425 c = c % p 

1426 

1427 if a == 0: 

1428 return linear_congruence(b, -c, p) 

1429 if p == 2: 

1430 roots = [] 

1431 if c % 2 == 0: 

1432 roots.append(0) 

1433 if (a + b + c) % 2 == 0: 

1434 roots.append(1) 

1435 return roots 

1436 if isprime(p): 

1437 inv_a = mod_inverse(a, p) 

1438 b *= inv_a 

1439 c *= inv_a 

1440 if b % 2 == 1: 

1441 b = b + p 

1442 d = ((b * b) // 4 - c) % p 

1443 y = sqrt_mod(d, p, all_roots=True) 

1444 res = set() 

1445 for i in y: 

1446 res.add((i - b // 2) % p) 

1447 return sorted(res) 

1448 y = sqrt_mod(b * b - 4 * a * c, 4 * a * p, all_roots=True) 

1449 res = set() 

1450 for i in y: 

1451 root = linear_congruence(2 * a, i - b, 4 * a * p) 

1452 for j in root: 

1453 res.add(j % p) 

1454 return sorted(res) 

1455 

1456 

1457def _polynomial_congruence_prime(coefficients, p): 

1458 """A helper function used by polynomial_congruence. 

1459 It returns the root of a polynomial modulo prime number 

1460 by naive search from [0, p). 

1461 

1462 Parameters 

1463 ========== 

1464 

1465 coefficients : list of integers 

1466 p : prime number 

1467 """ 

1468 

1469 roots = [] 

1470 rank = len(coefficients) 

1471 for i in range(0, p): 

1472 f_val = 0 

1473 for coeff in range(0,rank - 1): 

1474 f_val = (f_val + pow(i, int(rank - coeff - 1), p) * coefficients[coeff]) % p 

1475 f_val = f_val + coefficients[-1] 

1476 if f_val % p == 0: 

1477 roots.append(i) 

1478 return roots 

1479 

1480 

1481def _diff_poly(root, coefficients, p): 

1482 """A helper function used by polynomial_congruence. 

1483 It returns the derivative of the polynomial evaluated at the 

1484 root (mod p). 

1485 

1486 Parameters 

1487 ========== 

1488 

1489 coefficients : list of integers 

1490 p : prime number 

1491 root : integer 

1492 """ 

1493 

1494 diff = 0 

1495 rank = len(coefficients) 

1496 for coeff in range(0, rank - 1): 

1497 if not coefficients[coeff]: 

1498 continue 

1499 diff = (diff + pow(root, rank - coeff - 2, p)*(rank - coeff - 1)* 

1500 coefficients[coeff]) % p 

1501 return diff % p 

1502 

1503 

1504def _val_poly(root, coefficients, p): 

1505 """A helper function used by polynomial_congruence. 

1506 It returns value of the polynomial at root (mod p). 

1507 

1508 Parameters 

1509 ========== 

1510 

1511 coefficients : list of integers 

1512 p : prime number 

1513 root : integer 

1514 """ 

1515 rank = len(coefficients) 

1516 f_val = 0 

1517 for coeff in range(0, rank - 1): 

1518 f_val = (f_val + pow(root, rank - coeff - 1, p)* 

1519 coefficients[coeff]) % p 

1520 f_val = f_val + coefficients[-1] 

1521 return f_val % p 

1522 

1523 

1524def _valid_expr(expr): 

1525 """ 

1526 return coefficients of expr if it is a univariate polynomial 

1527 with integer coefficients else raise a ValueError. 

1528 """ 

1529 

1530 if not expr.is_polynomial(): 

1531 raise ValueError("The expression should be a polynomial") 

1532 polynomial = Poly(expr) 

1533 if not polynomial.is_univariate: 

1534 raise ValueError("The expression should be univariate") 

1535 if not polynomial.domain == ZZ: 

1536 raise ValueError("The expression should should have integer coefficients") 

1537 return polynomial.all_coeffs() 

1538 

1539 

1540def polynomial_congruence(expr, m): 

1541 """ 

1542 Find the solutions to a polynomial congruence equation modulo m. 

1543 

1544 Parameters 

1545 ========== 

1546 

1547 coefficients : Coefficients of the Polynomial 

1548 m : positive integer 

1549 

1550 Examples 

1551 ======== 

1552 

1553 >>> from sympy.ntheory import polynomial_congruence 

1554 >>> from sympy.abc import x 

1555 >>> expr = x**6 - 2*x**5 -35 

1556 >>> polynomial_congruence(expr, 6125) 

1557 [3257] 

1558 """ 

1559 coefficients = _valid_expr(expr) 

1560 coefficients = [num % m for num in coefficients] 

1561 rank = len(coefficients) 

1562 if rank == 3: 

1563 return quadratic_congruence(*coefficients, m) 

1564 if rank == 2: 

1565 return quadratic_congruence(0, *coefficients, m) 

1566 if coefficients[0] == 1 and 1 + coefficients[-1] == sum(coefficients): 

1567 return nthroot_mod(-coefficients[-1], rank - 1, m, True) 

1568 if isprime(m): 

1569 return _polynomial_congruence_prime(coefficients, m) 

1570 return _help(m, 

1571 lambda p: _polynomial_congruence_prime(coefficients, p), 

1572 lambda root, p: _diff_poly(root, coefficients, p), 

1573 lambda root, p: _val_poly(root, coefficients, p))