fix series expansion of floor and ceiling at arbitrary point
[sympy.git] / sympy / functions / elementary / integers.py
blob8def32fea36232b72575dc67c0bf031d2136235d
2 from sympy.core.basic import Basic, S, C, sympify
3 from sympy.core.function import Lambda, Function
5 from sympy.core.evalf import get_integer_part, PrecisionExhausted
7 ###############################################################################
8 ######################### FLOOR and CEILING FUNCTIONS #########################
9 ###############################################################################
11 class RoundFunction(Function):
13 nargs = 1
15 @classmethod
16 def canonize(cls, arg):
17 if arg.is_integer:
18 return arg
19 if arg.is_imaginary:
20 return cls(C.im(arg))*S.ImaginaryUnit
22 v = cls._eval_number(arg)
23 if v is not None:
24 return v
26 # Integral, numerical, symbolic part
27 ipart = npart = spart = S.Zero
29 # Extract integral (or complex integral) terms
30 if arg.is_Add:
31 terms = arg.args
32 else:
33 terms = [arg]
35 for t in terms:
36 if t.is_integer or (t.is_imaginary and C.im(t).is_integer):
37 ipart += t
38 elif t.atoms(C.Symbol):
39 spart += t
40 else:
41 npart += t
43 if not (npart or spart):
44 return ipart
46 # Evaluate npart numerically if independent of spart
47 orthogonal = (npart.is_real and spart.is_imaginary) or \
48 (npart.is_imaginary and spart.is_real)
49 if npart and ((not spart) or orthogonal):
50 try:
51 re, im = get_integer_part(npart, cls._dir, {}, return_ints=True)
52 ipart += C.Integer(re) + C.Integer(im)*S.ImaginaryUnit
53 npart = S.Zero
54 except (PrecisionExhausted, NotImplementedError):
55 pass
57 spart = npart + spart
58 if not spart:
59 return ipart
60 elif spart.is_imaginary:
61 return ipart + cls(C.im(spart),evaluate=False)*S.ImaginaryUnit
62 else:
63 return ipart + cls(spart, evaluate=False)
65 def _eval_is_bounded(self):
66 return self.args[0].is_bounded
68 def _eval_is_real(self):
69 return self.args[0].is_real
71 def _eval_is_integer(self):
72 return self.args[0].is_real
74 class floor(RoundFunction):
75 """
76 Floor is a univariate function which returns the largest integer
77 value not greater than its argument. However this implementaion
78 generalizes floor to complex numbers.
80 More information can be found in "Concrete mathematics" by Graham,
81 pp. 87 or visit http://mathworld.wolfram.com/FloorFunction.html.
83 >>> from sympy import *
84 >>> floor(17)
86 >>> floor(Rational(23, 10))
88 >>> floor(2*E)
90 >>> floor(-Real(0.567))
92 >>> floor(-I/2)
94 """
95 _dir = -1
97 @classmethod
98 def _eval_number(cls, arg):
99 if arg.is_Number:
100 if arg.is_Rational:
101 if not arg.q:
102 return arg
103 return C.Integer(arg.p // arg.q)
104 elif arg.is_Real:
105 return C.Integer(int(arg.floor()))
106 if arg.is_NumberSymbol:
107 return arg.approximation_interval(C.Integer)[0]
109 def _eval_nseries(self, x, x0, n):
110 r = self.subs(x, x0)
111 args = self.args[0]
112 if args.subs(x, x0) == r:
113 direction = (args.subs(x, x+x0) - args.subs(x, x0)).leadterm(x)[0]
114 if direction.is_positive:
115 return r
116 else:
117 return r-1
118 else:
119 return r
122 class ceiling(RoundFunction):
124 Ceiling is a univariate function which returns the smallest integer
125 value not less than its argument. Ceiling function is generalized
126 in this implementation to complex numbers.
128 More information can be found in "Concrete mathematics" by Graham,
129 pp. 87 or visit http://mathworld.wolfram.com/CeilingFunction.html.
131 >>> from sympy import *
132 >>> ceiling(17)
134 >>> ceiling(Rational(23, 10))
136 >>> ceiling(2*E)
138 >>> ceiling(-Real(0.567))
140 >>> ceiling(I/2)
143 _dir = 1
145 @classmethod
146 def _eval_number(cls, arg):
147 if arg.is_Number:
148 if arg.is_Rational:
149 if not arg.q:
150 return arg
151 return -C.Integer(-arg.p // arg.q)
152 elif arg.is_Real:
153 return C.Integer(int(arg.ceiling()))
154 if arg.is_NumberSymbol:
155 return arg.approximation_interval(C.Integer)[1]
157 def _eval_nseries(self, x, x0, n):
158 r = self.subs(x, x0)
159 args = self.args[0]
160 if args.subs(x,x0) == r:
161 direction = (args.subs(x, x+x0) - args.subs(x, x0)).leadterm(x)[0]
162 if direction.is_positive:
163 return r+1
164 else:
165 return r
166 else:
167 return r