Coverage for /usr/lib/python3/dist-packages/sympy/polys/domains/modularinteger.py: 35%
133 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
1"""Implementation of :class:`ModularInteger` class. """
3from __future__ import annotations
4from typing import Any
6import operator
8from sympy.polys.polyutils import PicklableWithSlots
9from sympy.polys.polyerrors import CoercionFailed
10from sympy.polys.domains.domainelement import DomainElement
12from sympy.utilities import public
14@public
15class ModularInteger(PicklableWithSlots, DomainElement):
16 """A class representing a modular integer. """
18 mod, dom, sym, _parent = None, None, None, None
20 __slots__ = ('val',)
22 def parent(self):
23 return self._parent
25 def __init__(self, val):
26 if isinstance(val, self.__class__):
27 self.val = val.val % self.mod
28 else:
29 self.val = self.dom.convert(val) % self.mod
31 def __hash__(self):
32 return hash((self.val, self.mod))
34 def __repr__(self):
35 return "%s(%s)" % (self.__class__.__name__, self.val)
37 def __str__(self):
38 return "%s mod %s" % (self.val, self.mod)
40 def __int__(self):
41 return int(self.to_int())
43 def to_int(self):
44 if self.sym:
45 if self.val <= self.mod // 2:
46 return self.val
47 else:
48 return self.val - self.mod
49 else:
50 return self.val
52 def __pos__(self):
53 return self
55 def __neg__(self):
56 return self.__class__(-self.val)
58 @classmethod
59 def _get_val(cls, other):
60 if isinstance(other, cls):
61 return other.val
62 else:
63 try:
64 return cls.dom.convert(other)
65 except CoercionFailed:
66 return None
68 def __add__(self, other):
69 val = self._get_val(other)
71 if val is not None:
72 return self.__class__(self.val + val)
73 else:
74 return NotImplemented
76 def __radd__(self, other):
77 return self.__add__(other)
79 def __sub__(self, other):
80 val = self._get_val(other)
82 if val is not None:
83 return self.__class__(self.val - val)
84 else:
85 return NotImplemented
87 def __rsub__(self, other):
88 return (-self).__add__(other)
90 def __mul__(self, other):
91 val = self._get_val(other)
93 if val is not None:
94 return self.__class__(self.val * val)
95 else:
96 return NotImplemented
98 def __rmul__(self, other):
99 return self.__mul__(other)
101 def __truediv__(self, other):
102 val = self._get_val(other)
104 if val is not None:
105 return self.__class__(self.val * self._invert(val))
106 else:
107 return NotImplemented
109 def __rtruediv__(self, other):
110 return self.invert().__mul__(other)
112 def __mod__(self, other):
113 val = self._get_val(other)
115 if val is not None:
116 return self.__class__(self.val % val)
117 else:
118 return NotImplemented
120 def __rmod__(self, other):
121 val = self._get_val(other)
123 if val is not None:
124 return self.__class__(val % self.val)
125 else:
126 return NotImplemented
128 def __pow__(self, exp):
129 if not exp:
130 return self.__class__(self.dom.one)
132 if exp < 0:
133 val, exp = self.invert().val, -exp
134 else:
135 val = self.val
137 return self.__class__(pow(val, int(exp), self.mod))
139 def _compare(self, other, op):
140 val = self._get_val(other)
142 if val is not None:
143 return op(self.val, val % self.mod)
144 else:
145 return NotImplemented
147 def __eq__(self, other):
148 return self._compare(other, operator.eq)
150 def __ne__(self, other):
151 return self._compare(other, operator.ne)
153 def __lt__(self, other):
154 return self._compare(other, operator.lt)
156 def __le__(self, other):
157 return self._compare(other, operator.le)
159 def __gt__(self, other):
160 return self._compare(other, operator.gt)
162 def __ge__(self, other):
163 return self._compare(other, operator.ge)
165 def __bool__(self):
166 return bool(self.val)
168 @classmethod
169 def _invert(cls, value):
170 return cls.dom.invert(value, cls.mod)
172 def invert(self):
173 return self.__class__(self._invert(self.val))
175_modular_integer_cache: dict[tuple[Any, Any, Any], type[ModularInteger]] = {}
177def ModularIntegerFactory(_mod, _dom, _sym, parent):
178 """Create custom class for specific integer modulus."""
179 try:
180 _mod = _dom.convert(_mod)
181 except CoercionFailed:
182 ok = False
183 else:
184 ok = True
186 if not ok or _mod < 1:
187 raise ValueError("modulus must be a positive integer, got %s" % _mod)
189 key = _mod, _dom, _sym
191 try:
192 cls = _modular_integer_cache[key]
193 except KeyError:
194 class cls(ModularInteger):
195 mod, dom, sym = _mod, _dom, _sym
196 _parent = parent
198 if _sym:
199 cls.__name__ = "SymmetricModularIntegerMod%s" % _mod
200 else:
201 cls.__name__ = "ModularIntegerMod%s" % _mod
203 _modular_integer_cache[key] = cls
205 return cls