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

1"""Implementation of :class:`ModularInteger` class. """ 

2 

3from __future__ import annotations 

4from typing import Any 

5 

6import operator 

7 

8from sympy.polys.polyutils import PicklableWithSlots 

9from sympy.polys.polyerrors import CoercionFailed 

10from sympy.polys.domains.domainelement import DomainElement 

11 

12from sympy.utilities import public 

13 

14@public 

15class ModularInteger(PicklableWithSlots, DomainElement): 

16 """A class representing a modular integer. """ 

17 

18 mod, dom, sym, _parent = None, None, None, None 

19 

20 __slots__ = ('val',) 

21 

22 def parent(self): 

23 return self._parent 

24 

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 

30 

31 def __hash__(self): 

32 return hash((self.val, self.mod)) 

33 

34 def __repr__(self): 

35 return "%s(%s)" % (self.__class__.__name__, self.val) 

36 

37 def __str__(self): 

38 return "%s mod %s" % (self.val, self.mod) 

39 

40 def __int__(self): 

41 return int(self.to_int()) 

42 

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 

51 

52 def __pos__(self): 

53 return self 

54 

55 def __neg__(self): 

56 return self.__class__(-self.val) 

57 

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 

67 

68 def __add__(self, other): 

69 val = self._get_val(other) 

70 

71 if val is not None: 

72 return self.__class__(self.val + val) 

73 else: 

74 return NotImplemented 

75 

76 def __radd__(self, other): 

77 return self.__add__(other) 

78 

79 def __sub__(self, other): 

80 val = self._get_val(other) 

81 

82 if val is not None: 

83 return self.__class__(self.val - val) 

84 else: 

85 return NotImplemented 

86 

87 def __rsub__(self, other): 

88 return (-self).__add__(other) 

89 

90 def __mul__(self, other): 

91 val = self._get_val(other) 

92 

93 if val is not None: 

94 return self.__class__(self.val * val) 

95 else: 

96 return NotImplemented 

97 

98 def __rmul__(self, other): 

99 return self.__mul__(other) 

100 

101 def __truediv__(self, other): 

102 val = self._get_val(other) 

103 

104 if val is not None: 

105 return self.__class__(self.val * self._invert(val)) 

106 else: 

107 return NotImplemented 

108 

109 def __rtruediv__(self, other): 

110 return self.invert().__mul__(other) 

111 

112 def __mod__(self, other): 

113 val = self._get_val(other) 

114 

115 if val is not None: 

116 return self.__class__(self.val % val) 

117 else: 

118 return NotImplemented 

119 

120 def __rmod__(self, other): 

121 val = self._get_val(other) 

122 

123 if val is not None: 

124 return self.__class__(val % self.val) 

125 else: 

126 return NotImplemented 

127 

128 def __pow__(self, exp): 

129 if not exp: 

130 return self.__class__(self.dom.one) 

131 

132 if exp < 0: 

133 val, exp = self.invert().val, -exp 

134 else: 

135 val = self.val 

136 

137 return self.__class__(pow(val, int(exp), self.mod)) 

138 

139 def _compare(self, other, op): 

140 val = self._get_val(other) 

141 

142 if val is not None: 

143 return op(self.val, val % self.mod) 

144 else: 

145 return NotImplemented 

146 

147 def __eq__(self, other): 

148 return self._compare(other, operator.eq) 

149 

150 def __ne__(self, other): 

151 return self._compare(other, operator.ne) 

152 

153 def __lt__(self, other): 

154 return self._compare(other, operator.lt) 

155 

156 def __le__(self, other): 

157 return self._compare(other, operator.le) 

158 

159 def __gt__(self, other): 

160 return self._compare(other, operator.gt) 

161 

162 def __ge__(self, other): 

163 return self._compare(other, operator.ge) 

164 

165 def __bool__(self): 

166 return bool(self.val) 

167 

168 @classmethod 

169 def _invert(cls, value): 

170 return cls.dom.invert(value, cls.mod) 

171 

172 def invert(self): 

173 return self.__class__(self._invert(self.val)) 

174 

175_modular_integer_cache: dict[tuple[Any, Any, Any], type[ModularInteger]] = {} 

176 

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 

185 

186 if not ok or _mod < 1: 

187 raise ValueError("modulus must be a positive integer, got %s" % _mod) 

188 

189 key = _mod, _dom, _sym 

190 

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 

197 

198 if _sym: 

199 cls.__name__ = "SymmetricModularIntegerMod%s" % _mod 

200 else: 

201 cls.__name__ = "ModularIntegerMod%s" % _mod 

202 

203 _modular_integer_cache[key] = cls 

204 

205 return cls