Coverage for /usr/lib/python3/dist-packages/sympy/ntheory/modular.py: 16%
74 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from functools import reduce
2from math import prod
4from sympy.core.numbers import igcdex, igcd
5from sympy.ntheory.primetest import isprime
6from sympy.polys.domains import ZZ
7from sympy.polys.galoistools import gf_crt, gf_crt1, gf_crt2
8from sympy.utilities.misc import as_int
11def symmetric_residue(a, m):
12 """Return the residual mod m such that it is within half of the modulus.
14 >>> from sympy.ntheory.modular import symmetric_residue
15 >>> symmetric_residue(1, 6)
16 1
17 >>> symmetric_residue(4, 6)
18 -2
19 """
20 if a <= m // 2:
21 return a
22 return a - m
25def crt(m, v, symmetric=False, check=True):
26 r"""Chinese Remainder Theorem.
28 The moduli in m are assumed to be pairwise coprime. The output
29 is then an integer f, such that f = v_i mod m_i for each pair out
30 of v and m. If ``symmetric`` is False a positive integer will be
31 returned, else \|f\| will be less than or equal to the LCM of the
32 moduli, and thus f may be negative.
34 If the moduli are not co-prime the correct result will be returned
35 if/when the test of the result is found to be incorrect. This result
36 will be None if there is no solution.
38 The keyword ``check`` can be set to False if it is known that the moduli
39 are coprime.
41 Examples
42 ========
44 As an example consider a set of residues ``U = [49, 76, 65]``
45 and a set of moduli ``M = [99, 97, 95]``. Then we have::
47 >>> from sympy.ntheory.modular import crt
49 >>> crt([99, 97, 95], [49, 76, 65])
50 (639985, 912285)
52 This is the correct result because::
54 >>> [639985 % m for m in [99, 97, 95]]
55 [49, 76, 65]
57 If the moduli are not co-prime, you may receive an incorrect result
58 if you use ``check=False``:
60 >>> crt([12, 6, 17], [3, 4, 2], check=False)
61 (954, 1224)
62 >>> [954 % m for m in [12, 6, 17]]
63 [6, 0, 2]
64 >>> crt([12, 6, 17], [3, 4, 2]) is None
65 True
66 >>> crt([3, 6], [2, 5])
67 (5, 6)
69 Note: the order of gf_crt's arguments is reversed relative to crt,
70 and that solve_congruence takes residue, modulus pairs.
72 Programmer's note: rather than checking that all pairs of moduli share
73 no GCD (an O(n**2) test) and rather than factoring all moduli and seeing
74 that there is no factor in common, a check that the result gives the
75 indicated residuals is performed -- an O(n) operation.
77 See Also
78 ========
80 solve_congruence
81 sympy.polys.galoistools.gf_crt : low level crt routine used by this routine
82 """
83 if check:
84 m = list(map(as_int, m))
85 v = list(map(as_int, v))
87 result = gf_crt(v, m, ZZ)
88 mm = prod(m)
90 if check:
91 if not all(v % m == result % m for v, m in zip(v, m)):
92 result = solve_congruence(*list(zip(v, m)),
93 check=False, symmetric=symmetric)
94 if result is None:
95 return result
96 result, mm = result
98 if symmetric:
99 return symmetric_residue(result, mm), mm
100 return result, mm
103def crt1(m):
104 """First part of Chinese Remainder Theorem, for multiple application.
106 Examples
107 ========
109 >>> from sympy.ntheory.modular import crt1
110 >>> crt1([18, 42, 6])
111 (4536, [252, 108, 756], [0, 2, 0])
112 """
114 return gf_crt1(m, ZZ)
117def crt2(m, v, mm, e, s, symmetric=False):
118 """Second part of Chinese Remainder Theorem, for multiple application.
120 Examples
121 ========
123 >>> from sympy.ntheory.modular import crt1, crt2
124 >>> mm, e, s = crt1([18, 42, 6])
125 >>> crt2([18, 42, 6], [0, 0, 0], mm, e, s)
126 (0, 4536)
127 """
129 result = gf_crt2(v, m, mm, e, s, ZZ)
131 if symmetric:
132 return symmetric_residue(result, mm), mm
133 return result, mm
136def solve_congruence(*remainder_modulus_pairs, **hint):
137 """Compute the integer ``n`` that has the residual ``ai`` when it is
138 divided by ``mi`` where the ``ai`` and ``mi`` are given as pairs to
139 this function: ((a1, m1), (a2, m2), ...). If there is no solution,
140 return None. Otherwise return ``n`` and its modulus.
142 The ``mi`` values need not be co-prime. If it is known that the moduli are
143 not co-prime then the hint ``check`` can be set to False (default=True) and
144 the check for a quicker solution via crt() (valid when the moduli are
145 co-prime) will be skipped.
147 If the hint ``symmetric`` is True (default is False), the value of ``n``
148 will be within 1/2 of the modulus, possibly negative.
150 Examples
151 ========
153 >>> from sympy.ntheory.modular import solve_congruence
155 What number is 2 mod 3, 3 mod 5 and 2 mod 7?
157 >>> solve_congruence((2, 3), (3, 5), (2, 7))
158 (23, 105)
159 >>> [23 % m for m in [3, 5, 7]]
160 [2, 3, 2]
162 If you prefer to work with all remainder in one list and
163 all moduli in another, send the arguments like this:
165 >>> solve_congruence(*zip((2, 3, 2), (3, 5, 7)))
166 (23, 105)
168 The moduli need not be co-prime; in this case there may or
169 may not be a solution:
171 >>> solve_congruence((2, 3), (4, 6)) is None
172 True
174 >>> solve_congruence((2, 3), (5, 6))
175 (5, 6)
177 The symmetric flag will make the result be within 1/2 of the modulus:
179 >>> solve_congruence((2, 3), (5, 6), symmetric=True)
180 (-1, 6)
182 See Also
183 ========
185 crt : high level routine implementing the Chinese Remainder Theorem
187 """
188 def combine(c1, c2):
189 """Return the tuple (a, m) which satisfies the requirement
190 that n = a + i*m satisfy n = a1 + j*m1 and n = a2 = k*m2.
192 References
193 ==========
195 .. [1] https://en.wikipedia.org/wiki/Method_of_successive_substitution
196 """
197 a1, m1 = c1
198 a2, m2 = c2
199 a, b, c = m1, a2 - a1, m2
200 g = reduce(igcd, [a, b, c])
201 a, b, c = [i//g for i in [a, b, c]]
202 if a != 1:
203 inv_a, _, g = igcdex(a, c)
204 if g != 1:
205 return None
206 b *= inv_a
207 a, m = a1 + m1*b, m1*c
208 return a, m
210 rm = remainder_modulus_pairs
211 symmetric = hint.get('symmetric', False)
213 if hint.get('check', True):
214 rm = [(as_int(r), as_int(m)) for r, m in rm]
216 # ignore redundant pairs but raise an error otherwise; also
217 # make sure that a unique set of bases is sent to gf_crt if
218 # they are all prime.
219 #
220 # The routine will work out less-trivial violations and
221 # return None, e.g. for the pairs (1,3) and (14,42) there
222 # is no answer because 14 mod 42 (having a gcd of 14) implies
223 # (14/2) mod (42/2), (14/7) mod (42/7) and (14/14) mod (42/14)
224 # which, being 0 mod 3, is inconsistent with 1 mod 3. But to
225 # preprocess the input beyond checking of another pair with 42
226 # or 3 as the modulus (for this example) is not necessary.
227 uniq = {}
228 for r, m in rm:
229 r %= m
230 if m in uniq:
231 if r != uniq[m]:
232 return None
233 continue
234 uniq[m] = r
235 rm = [(r, m) for m, r in uniq.items()]
236 del uniq
238 # if the moduli are co-prime, the crt will be significantly faster;
239 # checking all pairs for being co-prime gets to be slow but a prime
240 # test is a good trade-off
241 if all(isprime(m) for r, m in rm):
242 r, m = list(zip(*rm))
243 return crt(m, r, symmetric=symmetric, check=False)
245 rv = (0, 1)
246 for rmi in rm:
247 rv = combine(rv, rmi)
248 if rv is None:
249 break
250 n, m = rv
251 n = n % m
252 else:
253 if symmetric:
254 return symmetric_residue(n, m), m
255 return n, m