Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

from __future__ import print_function, division 

 

from sympy.core.numbers import nan 

from .function import Function 

 

 

class Mod(Function): 

"""Represents a modulo operation on symbolic expressions. 

 

Receives two arguments, dividend p and divisor q. 

 

The convention used is the same as Python's: the remainder always has the 

same sign as the divisor. 

 

Examples 

======== 

 

>>> from sympy.abc import x, y 

>>> x**2 % y 

Mod(x**2, y) 

>>> _.subs({x: 5, y: 6}) 

1 

 

""" 

 

@classmethod 

def eval(cls, p, q): 

from sympy.core.add import Add 

from sympy.core.mul import Mul 

from sympy.core.singleton import S 

from sympy.core.exprtools import gcd_terms 

from sympy.polys.polytools import gcd 

 

def doit(p, q): 

"""Try to return p % q if both are numbers or +/-p is known 

to be less than or equal q. 

""" 

 

if p.is_infinite or q.is_infinite or p is nan or q is nan: 

return nan 

if (p == q or p == -q or 

p.is_Pow and p.exp.is_Integer and p.base == q or 

p.is_integer and q == 1): 

return S.Zero 

 

if q.is_Number: 

if p.is_Number: 

return (p % q) 

if q == 2: 

if p.is_even: 

return S.Zero 

elif p.is_odd: 

return S.One 

 

# by ratio 

r = p/q 

try: 

d = int(r) 

except TypeError: 

pass 

else: 

if type(d) is int: 

rv = p - d*q 

if (rv*q < 0) == True: 

rv += q 

return rv 

 

# by difference 

d = p - q 

if d.is_negative: 

if q.is_negative: 

return d 

elif q.is_positive: 

return p 

 

rv = doit(p, q) 

if rv is not None: 

return rv 

 

# denest 

if p.func is cls: 

# easy 

qinner = p.args[1] 

if qinner == q: 

return p 

# XXX other possibilities? 

 

# extract gcd; any further simplification should be done by the user 

G = gcd(p, q) 

if G != 1: 

p, q = [ 

gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] 

pwas, qwas = p, q 

 

# simplify terms 

# (x + y + 2) % x -> Mod(y + 2, x) 

if p.is_Add: 

args = [] 

for i in p.args: 

a = cls(i, q) 

if a.count(cls) > i.count(cls): 

args.append(i) 

else: 

args.append(a) 

if args != list(p.args): 

p = Add(*args) 

 

else: 

# handle coefficients if they are not Rational 

# since those are not handled by factor_terms 

# e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) 

cp, p = p.as_coeff_Mul() 

cq, q = q.as_coeff_Mul() 

ok = False 

if not cp.is_Rational or not cq.is_Rational: 

r = cp % cq 

if r == 0: 

G *= cq 

p *= int(cp/cq) 

ok = True 

if not ok: 

p = cp*p 

q = cq*q 

 

# simple -1 extraction 

if p.could_extract_minus_sign() and q.could_extract_minus_sign(): 

G, p, q = [-i for i in (G, p, q)] 

 

# check again to see if p and q can now be handled as numbers 

rv = doit(p, q) 

if rv is not None: 

return rv*G 

 

# put 1.0 from G on inside 

if G.is_Float and G == 1: 

p *= G 

return cls(p, q, evaluate=False) 

elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: 

p = G.args[0]*p 

G = Mul._from_args(G.args[1:]) 

return G*cls(p, q, evaluate=(p, q) != (pwas, qwas)) 

 

def _eval_is_integer(self): 

from sympy.core.logic import fuzzy_and, fuzzy_not 

p, q = self.args 

if fuzzy_and([p.is_integer, q.is_integer, fuzzy_not(q.is_zero)]): 

return True 

 

def _eval_is_nonnegative(self): 

if self.args[1].is_positive: 

return True 

 

def _eval_is_nonpositive(self): 

if self.args[1].is_negative: 

return True