mod.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. from .add import Add
  2. from .exprtools import gcd_terms
  3. from .function import Function
  4. from .kind import NumberKind
  5. from .logic import fuzzy_and, fuzzy_not
  6. from .mul import Mul
  7. from .numbers import equal_valued
  8. from .singleton import S
  9. class Mod(Function):
  10. """Represents a modulo operation on symbolic expressions.
  11. Parameters
  12. ==========
  13. p : Expr
  14. Dividend.
  15. q : Expr
  16. Divisor.
  17. Notes
  18. =====
  19. The convention used is the same as Python's: the remainder always has the
  20. same sign as the divisor.
  21. Examples
  22. ========
  23. >>> from sympy.abc import x, y
  24. >>> x**2 % y
  25. Mod(x**2, y)
  26. >>> _.subs({x: 5, y: 6})
  27. 1
  28. """
  29. kind = NumberKind
  30. @classmethod
  31. def eval(cls, p, q):
  32. def number_eval(p, q):
  33. """Try to return p % q if both are numbers or +/-p is known
  34. to be less than or equal q.
  35. """
  36. if q.is_zero:
  37. raise ZeroDivisionError("Modulo by zero")
  38. if p is S.NaN or q is S.NaN or p.is_finite is False or q.is_finite is False:
  39. return S.NaN
  40. if p is S.Zero or p in (q, -q) or (p.is_integer and q == 1):
  41. return S.Zero
  42. if q.is_Number:
  43. if p.is_Number:
  44. return p%q
  45. if q == 2:
  46. if p.is_even:
  47. return S.Zero
  48. elif p.is_odd:
  49. return S.One
  50. if hasattr(p, '_eval_Mod'):
  51. rv = getattr(p, '_eval_Mod')(q)
  52. if rv is not None:
  53. return rv
  54. # by ratio
  55. r = p/q
  56. if r.is_integer:
  57. return S.Zero
  58. try:
  59. d = int(r)
  60. except TypeError:
  61. pass
  62. else:
  63. if isinstance(d, int):
  64. rv = p - d*q
  65. if (rv*q < 0) == True:
  66. rv += q
  67. return rv
  68. # by difference
  69. # -2|q| < p < 2|q|
  70. d = abs(p)
  71. for _ in range(2):
  72. d -= abs(q)
  73. if d.is_negative:
  74. if q.is_positive:
  75. if p.is_positive:
  76. return d + q
  77. elif p.is_negative:
  78. return -d
  79. elif q.is_negative:
  80. if p.is_positive:
  81. return d
  82. elif p.is_negative:
  83. return -d + q
  84. break
  85. rv = number_eval(p, q)
  86. if rv is not None:
  87. return rv
  88. # denest
  89. if isinstance(p, cls):
  90. qinner = p.args[1]
  91. if qinner % q == 0:
  92. return cls(p.args[0], q)
  93. elif (qinner*(q - qinner)).is_nonnegative:
  94. # |qinner| < |q| and have same sign
  95. return p
  96. elif isinstance(-p, cls):
  97. qinner = (-p).args[1]
  98. if qinner % q == 0:
  99. return cls(-(-p).args[0], q)
  100. elif (qinner*(q + qinner)).is_nonpositive:
  101. # |qinner| < |q| and have different sign
  102. return p
  103. elif isinstance(p, Add):
  104. # separating into modulus and non modulus
  105. both_l = non_mod_l, mod_l = [], []
  106. for arg in p.args:
  107. both_l[isinstance(arg, cls)].append(arg)
  108. # if q same for all
  109. if mod_l and all(inner.args[1] == q for inner in mod_l):
  110. net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l])
  111. return cls(net, q)
  112. elif isinstance(p, Mul):
  113. # separating into modulus and non modulus
  114. both_l = non_mod_l, mod_l = [], []
  115. for arg in p.args:
  116. both_l[isinstance(arg, cls)].append(arg)
  117. if mod_l and all(inner.args[1] == q for inner in mod_l) and all(t.is_integer for t in p.args) and q.is_integer:
  118. # finding distributive term
  119. non_mod_l = [cls(x, q) for x in non_mod_l]
  120. mod = []
  121. non_mod = []
  122. for j in non_mod_l:
  123. if isinstance(j, cls):
  124. mod.append(j.args[0])
  125. else:
  126. non_mod.append(j)
  127. prod_mod = Mul(*mod)
  128. prod_non_mod = Mul(*non_mod)
  129. prod_mod1 = Mul(*[i.args[0] for i in mod_l])
  130. net = prod_mod1*prod_mod
  131. return prod_non_mod*cls(net, q)
  132. if q.is_Integer and q is not S.One:
  133. non_mod_l = [i % q if i.is_Integer and (i % q is not S.Zero) else i for
  134. i in non_mod_l]
  135. p = Mul(*(non_mod_l + mod_l))
  136. # XXX other possibilities?
  137. from sympy.polys.polyerrors import PolynomialError
  138. from sympy.polys.polytools import gcd
  139. # extract gcd; any further simplification should be done by the user
  140. try:
  141. G = gcd(p, q)
  142. if not equal_valued(G, 1):
  143. p, q = [gcd_terms(i/G, clear=False, fraction=False)
  144. for i in (p, q)]
  145. except PolynomialError: # issue 21373
  146. G = S.One
  147. pwas, qwas = p, q
  148. # simplify terms
  149. # (x + y + 2) % x -> Mod(y + 2, x)
  150. if p.is_Add:
  151. args = []
  152. for i in p.args:
  153. a = cls(i, q)
  154. if a.count(cls) > i.count(cls):
  155. args.append(i)
  156. else:
  157. args.append(a)
  158. if args != list(p.args):
  159. p = Add(*args)
  160. else:
  161. # handle coefficients if they are not Rational
  162. # since those are not handled by factor_terms
  163. # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y)
  164. cp, p = p.as_coeff_Mul()
  165. cq, q = q.as_coeff_Mul()
  166. ok = False
  167. if not cp.is_Rational or not cq.is_Rational:
  168. r = cp % cq
  169. if equal_valued(r, 0):
  170. G *= cq
  171. p *= int(cp/cq)
  172. ok = True
  173. if not ok:
  174. p = cp*p
  175. q = cq*q
  176. # simple -1 extraction
  177. if p.could_extract_minus_sign() and q.could_extract_minus_sign():
  178. G, p, q = [-i for i in (G, p, q)]
  179. # check again to see if p and q can now be handled as numbers
  180. rv = number_eval(p, q)
  181. if rv is not None:
  182. return rv*G
  183. # put 1.0 from G on inside
  184. if G.is_Float and equal_valued(G, 1):
  185. p *= G
  186. return cls(p, q, evaluate=False)
  187. elif G.is_Mul and G.args[0].is_Float and equal_valued(G.args[0], 1):
  188. p = G.args[0]*p
  189. G = Mul._from_args(G.args[1:])
  190. return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
  191. def _eval_is_integer(self):
  192. p, q = self.args
  193. if fuzzy_and([p.is_integer, q.is_integer, fuzzy_not(q.is_zero)]):
  194. return True
  195. def _eval_is_nonnegative(self):
  196. if self.args[1].is_positive:
  197. return True
  198. def _eval_is_nonpositive(self):
  199. if self.args[1].is_negative:
  200. return True
  201. def _eval_rewrite_as_floor(self, a, b, **kwargs):
  202. from sympy.functions.elementary.integers import floor
  203. return a - b*floor(a/b)