integers.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. from typing import Tuple as tTuple
  2. from sympy.core.basic import Basic
  3. from sympy.core.expr import Expr
  4. from sympy.core import Add, S
  5. from sympy.core.evalf import get_integer_part, PrecisionExhausted
  6. from sympy.core.function import Function
  7. from sympy.core.logic import fuzzy_or
  8. from sympy.core.numbers import Integer
  9. from sympy.core.relational import Gt, Lt, Ge, Le, Relational, is_eq
  10. from sympy.core.symbol import Symbol
  11. from sympy.core.sympify import _sympify
  12. from sympy.functions.elementary.complexes import im, re
  13. from sympy.multipledispatch import dispatch
  14. ###############################################################################
  15. ######################### FLOOR and CEILING FUNCTIONS #########################
  16. ###############################################################################
  17. class RoundFunction(Function):
  18. """Abstract base class for rounding functions."""
  19. args: tTuple[Expr]
  20. @classmethod
  21. def eval(cls, arg):
  22. v = cls._eval_number(arg)
  23. if v is not None:
  24. return v
  25. if arg.is_integer or arg.is_finite is False:
  26. return arg
  27. if arg.is_imaginary or (S.ImaginaryUnit*arg).is_real:
  28. i = im(arg)
  29. if not i.has(S.ImaginaryUnit):
  30. return cls(i)*S.ImaginaryUnit
  31. return cls(arg, evaluate=False)
  32. # Integral, numerical, symbolic part
  33. ipart = npart = spart = S.Zero
  34. # Extract integral (or complex integral) terms
  35. terms = Add.make_args(arg)
  36. for t in terms:
  37. if t.is_integer or (t.is_imaginary and im(t).is_integer):
  38. ipart += t
  39. elif t.has(Symbol):
  40. spart += t
  41. else:
  42. npart += t
  43. if not (npart or spart):
  44. return ipart
  45. # Evaluate npart numerically if independent of spart
  46. if npart and (
  47. not spart or
  48. npart.is_real and (spart.is_imaginary or (S.ImaginaryUnit*spart).is_real) or
  49. npart.is_imaginary and spart.is_real):
  50. try:
  51. r, i = get_integer_part(
  52. npart, cls._dir, {}, return_ints=True)
  53. ipart += Integer(r) + Integer(i)*S.ImaginaryUnit
  54. npart = S.Zero
  55. except (PrecisionExhausted, NotImplementedError):
  56. pass
  57. spart += npart
  58. if not spart:
  59. return ipart
  60. elif spart.is_imaginary or (S.ImaginaryUnit*spart).is_real:
  61. return ipart + cls(im(spart), evaluate=False)*S.ImaginaryUnit
  62. elif isinstance(spart, (floor, ceiling)):
  63. return ipart + spart
  64. else:
  65. return ipart + cls(spart, evaluate=False)
  66. @classmethod
  67. def _eval_number(cls, arg):
  68. raise NotImplementedError()
  69. def _eval_is_finite(self):
  70. return self.args[0].is_finite
  71. def _eval_is_real(self):
  72. return self.args[0].is_real
  73. def _eval_is_integer(self):
  74. return self.args[0].is_real
  75. class floor(RoundFunction):
  76. """
  77. Floor is a univariate function which returns the largest integer
  78. value not greater than its argument. This implementation
  79. generalizes floor to complex numbers by taking the floor of the
  80. real and imaginary parts separately.
  81. Examples
  82. ========
  83. >>> from sympy import floor, E, I, S, Float, Rational
  84. >>> floor(17)
  85. 17
  86. >>> floor(Rational(23, 10))
  87. 2
  88. >>> floor(2*E)
  89. 5
  90. >>> floor(-Float(0.567))
  91. -1
  92. >>> floor(-I/2)
  93. -I
  94. >>> floor(S(5)/2 + 5*I/2)
  95. 2 + 2*I
  96. See Also
  97. ========
  98. sympy.functions.elementary.integers.ceiling
  99. References
  100. ==========
  101. .. [1] "Concrete mathematics" by Graham, pp. 87
  102. .. [2] https://mathworld.wolfram.com/FloorFunction.html
  103. """
  104. _dir = -1
  105. @classmethod
  106. def _eval_number(cls, arg):
  107. if arg.is_Number:
  108. return arg.floor()
  109. elif any(isinstance(i, j)
  110. for i in (arg, -arg) for j in (floor, ceiling)):
  111. return arg
  112. if arg.is_NumberSymbol:
  113. return arg.approximation_interval(Integer)[0]
  114. def _eval_as_leading_term(self, x, logx=None, cdir=0):
  115. from sympy.calculus.accumulationbounds import AccumBounds
  116. arg = self.args[0]
  117. arg0 = arg.subs(x, 0)
  118. r = self.subs(x, 0)
  119. if arg0 is S.NaN or isinstance(arg0, AccumBounds):
  120. arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
  121. r = floor(arg0)
  122. if arg0.is_finite:
  123. if arg0 == r:
  124. ndir = arg.dir(x, cdir=cdir)
  125. return r - 1 if ndir.is_negative else r
  126. else:
  127. return r
  128. return arg.as_leading_term(x, logx=logx, cdir=cdir)
  129. def _eval_nseries(self, x, n, logx, cdir=0):
  130. arg = self.args[0]
  131. arg0 = arg.subs(x, 0)
  132. r = self.subs(x, 0)
  133. if arg0 is S.NaN:
  134. arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
  135. r = floor(arg0)
  136. if arg0.is_infinite:
  137. from sympy.calculus.accumulationbounds import AccumBounds
  138. from sympy.series.order import Order
  139. s = arg._eval_nseries(x, n, logx, cdir)
  140. o = Order(1, (x, 0)) if n <= 0 else AccumBounds(-1, 0)
  141. return s + o
  142. if arg0 == r:
  143. ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
  144. return r - 1 if ndir.is_negative else r
  145. else:
  146. return r
  147. def _eval_is_negative(self):
  148. return self.args[0].is_negative
  149. def _eval_is_nonnegative(self):
  150. return self.args[0].is_nonnegative
  151. def _eval_rewrite_as_ceiling(self, arg, **kwargs):
  152. return -ceiling(-arg)
  153. def _eval_rewrite_as_frac(self, arg, **kwargs):
  154. return arg - frac(arg)
  155. def __le__(self, other):
  156. other = S(other)
  157. if self.args[0].is_real:
  158. if other.is_integer:
  159. return self.args[0] < other + 1
  160. if other.is_number and other.is_real:
  161. return self.args[0] < ceiling(other)
  162. if self.args[0] == other and other.is_real:
  163. return S.true
  164. if other is S.Infinity and self.is_finite:
  165. return S.true
  166. return Le(self, other, evaluate=False)
  167. def __ge__(self, other):
  168. other = S(other)
  169. if self.args[0].is_real:
  170. if other.is_integer:
  171. return self.args[0] >= other
  172. if other.is_number and other.is_real:
  173. return self.args[0] >= ceiling(other)
  174. if self.args[0] == other and other.is_real:
  175. return S.false
  176. if other is S.NegativeInfinity and self.is_finite:
  177. return S.true
  178. return Ge(self, other, evaluate=False)
  179. def __gt__(self, other):
  180. other = S(other)
  181. if self.args[0].is_real:
  182. if other.is_integer:
  183. return self.args[0] >= other + 1
  184. if other.is_number and other.is_real:
  185. return self.args[0] >= ceiling(other)
  186. if self.args[0] == other and other.is_real:
  187. return S.false
  188. if other is S.NegativeInfinity and self.is_finite:
  189. return S.true
  190. return Gt(self, other, evaluate=False)
  191. def __lt__(self, other):
  192. other = S(other)
  193. if self.args[0].is_real:
  194. if other.is_integer:
  195. return self.args[0] < other
  196. if other.is_number and other.is_real:
  197. return self.args[0] < ceiling(other)
  198. if self.args[0] == other and other.is_real:
  199. return S.false
  200. if other is S.Infinity and self.is_finite:
  201. return S.true
  202. return Lt(self, other, evaluate=False)
  203. @dispatch(floor, Expr)
  204. def _eval_is_eq(lhs, rhs): # noqa:F811
  205. return is_eq(lhs.rewrite(ceiling), rhs) or \
  206. is_eq(lhs.rewrite(frac),rhs)
  207. class ceiling(RoundFunction):
  208. """
  209. Ceiling is a univariate function which returns the smallest integer
  210. value not less than its argument. This implementation
  211. generalizes ceiling to complex numbers by taking the ceiling of the
  212. real and imaginary parts separately.
  213. Examples
  214. ========
  215. >>> from sympy import ceiling, E, I, S, Float, Rational
  216. >>> ceiling(17)
  217. 17
  218. >>> ceiling(Rational(23, 10))
  219. 3
  220. >>> ceiling(2*E)
  221. 6
  222. >>> ceiling(-Float(0.567))
  223. 0
  224. >>> ceiling(I/2)
  225. I
  226. >>> ceiling(S(5)/2 + 5*I/2)
  227. 3 + 3*I
  228. See Also
  229. ========
  230. sympy.functions.elementary.integers.floor
  231. References
  232. ==========
  233. .. [1] "Concrete mathematics" by Graham, pp. 87
  234. .. [2] https://mathworld.wolfram.com/CeilingFunction.html
  235. """
  236. _dir = 1
  237. @classmethod
  238. def _eval_number(cls, arg):
  239. if arg.is_Number:
  240. return arg.ceiling()
  241. elif any(isinstance(i, j)
  242. for i in (arg, -arg) for j in (floor, ceiling)):
  243. return arg
  244. if arg.is_NumberSymbol:
  245. return arg.approximation_interval(Integer)[1]
  246. def _eval_as_leading_term(self, x, logx=None, cdir=0):
  247. from sympy.calculus.accumulationbounds import AccumBounds
  248. arg = self.args[0]
  249. arg0 = arg.subs(x, 0)
  250. r = self.subs(x, 0)
  251. if arg0 is S.NaN or isinstance(arg0, AccumBounds):
  252. arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
  253. r = ceiling(arg0)
  254. if arg0.is_finite:
  255. if arg0 == r:
  256. ndir = arg.dir(x, cdir=cdir)
  257. return r if ndir.is_negative else r + 1
  258. else:
  259. return r
  260. return arg.as_leading_term(x, logx=logx, cdir=cdir)
  261. def _eval_nseries(self, x, n, logx, cdir=0):
  262. arg = self.args[0]
  263. arg0 = arg.subs(x, 0)
  264. r = self.subs(x, 0)
  265. if arg0 is S.NaN:
  266. arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
  267. r = ceiling(arg0)
  268. if arg0.is_infinite:
  269. from sympy.calculus.accumulationbounds import AccumBounds
  270. from sympy.series.order import Order
  271. s = arg._eval_nseries(x, n, logx, cdir)
  272. o = Order(1, (x, 0)) if n <= 0 else AccumBounds(0, 1)
  273. return s + o
  274. if arg0 == r:
  275. ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
  276. return r if ndir.is_negative else r + 1
  277. else:
  278. return r
  279. def _eval_rewrite_as_floor(self, arg, **kwargs):
  280. return -floor(-arg)
  281. def _eval_rewrite_as_frac(self, arg, **kwargs):
  282. return arg + frac(-arg)
  283. def _eval_is_positive(self):
  284. return self.args[0].is_positive
  285. def _eval_is_nonpositive(self):
  286. return self.args[0].is_nonpositive
  287. def __lt__(self, other):
  288. other = S(other)
  289. if self.args[0].is_real:
  290. if other.is_integer:
  291. return self.args[0] <= other - 1
  292. if other.is_number and other.is_real:
  293. return self.args[0] <= floor(other)
  294. if self.args[0] == other and other.is_real:
  295. return S.false
  296. if other is S.Infinity and self.is_finite:
  297. return S.true
  298. return Lt(self, other, evaluate=False)
  299. def __gt__(self, other):
  300. other = S(other)
  301. if self.args[0].is_real:
  302. if other.is_integer:
  303. return self.args[0] > other
  304. if other.is_number and other.is_real:
  305. return self.args[0] > floor(other)
  306. if self.args[0] == other and other.is_real:
  307. return S.false
  308. if other is S.NegativeInfinity and self.is_finite:
  309. return S.true
  310. return Gt(self, other, evaluate=False)
  311. def __ge__(self, other):
  312. other = S(other)
  313. if self.args[0].is_real:
  314. if other.is_integer:
  315. return self.args[0] > other - 1
  316. if other.is_number and other.is_real:
  317. return self.args[0] > floor(other)
  318. if self.args[0] == other and other.is_real:
  319. return S.true
  320. if other is S.NegativeInfinity and self.is_finite:
  321. return S.true
  322. return Ge(self, other, evaluate=False)
  323. def __le__(self, other):
  324. other = S(other)
  325. if self.args[0].is_real:
  326. if other.is_integer:
  327. return self.args[0] <= other
  328. if other.is_number and other.is_real:
  329. return self.args[0] <= floor(other)
  330. if self.args[0] == other and other.is_real:
  331. return S.false
  332. if other is S.Infinity and self.is_finite:
  333. return S.true
  334. return Le(self, other, evaluate=False)
  335. @dispatch(ceiling, Basic) # type:ignore
  336. def _eval_is_eq(lhs, rhs): # noqa:F811
  337. return is_eq(lhs.rewrite(floor), rhs) or is_eq(lhs.rewrite(frac),rhs)
  338. class frac(Function):
  339. r"""Represents the fractional part of x
  340. For real numbers it is defined [1]_ as
  341. .. math::
  342. x - \left\lfloor{x}\right\rfloor
  343. Examples
  344. ========
  345. >>> from sympy import Symbol, frac, Rational, floor, I
  346. >>> frac(Rational(4, 3))
  347. 1/3
  348. >>> frac(-Rational(4, 3))
  349. 2/3
  350. returns zero for integer arguments
  351. >>> n = Symbol('n', integer=True)
  352. >>> frac(n)
  353. 0
  354. rewrite as floor
  355. >>> x = Symbol('x')
  356. >>> frac(x).rewrite(floor)
  357. x - floor(x)
  358. for complex arguments
  359. >>> r = Symbol('r', real=True)
  360. >>> t = Symbol('t', real=True)
  361. >>> frac(t + I*r)
  362. I*frac(r) + frac(t)
  363. See Also
  364. ========
  365. sympy.functions.elementary.integers.floor
  366. sympy.functions.elementary.integers.ceiling
  367. References
  368. ===========
  369. .. [1] https://en.wikipedia.org/wiki/Fractional_part
  370. .. [2] https://mathworld.wolfram.com/FractionalPart.html
  371. """
  372. @classmethod
  373. def eval(cls, arg):
  374. from sympy.calculus.accumulationbounds import AccumBounds
  375. def _eval(arg):
  376. if arg in (S.Infinity, S.NegativeInfinity):
  377. return AccumBounds(0, 1)
  378. if arg.is_integer:
  379. return S.Zero
  380. if arg.is_number:
  381. if arg is S.NaN:
  382. return S.NaN
  383. elif arg is S.ComplexInfinity:
  384. return S.NaN
  385. else:
  386. return arg - floor(arg)
  387. return cls(arg, evaluate=False)
  388. terms = Add.make_args(arg)
  389. real, imag = S.Zero, S.Zero
  390. for t in terms:
  391. # Two checks are needed for complex arguments
  392. # see issue-7649 for details
  393. if t.is_imaginary or (S.ImaginaryUnit*t).is_real:
  394. i = im(t)
  395. if not i.has(S.ImaginaryUnit):
  396. imag += i
  397. else:
  398. real += t
  399. else:
  400. real += t
  401. real = _eval(real)
  402. imag = _eval(imag)
  403. return real + S.ImaginaryUnit*imag
  404. def _eval_rewrite_as_floor(self, arg, **kwargs):
  405. return arg - floor(arg)
  406. def _eval_rewrite_as_ceiling(self, arg, **kwargs):
  407. return arg + ceiling(-arg)
  408. def _eval_is_finite(self):
  409. return True
  410. def _eval_is_real(self):
  411. return self.args[0].is_extended_real
  412. def _eval_is_imaginary(self):
  413. return self.args[0].is_imaginary
  414. def _eval_is_integer(self):
  415. return self.args[0].is_integer
  416. def _eval_is_zero(self):
  417. return fuzzy_or([self.args[0].is_zero, self.args[0].is_integer])
  418. def _eval_is_negative(self):
  419. return False
  420. def __ge__(self, other):
  421. if self.is_extended_real:
  422. other = _sympify(other)
  423. # Check if other <= 0
  424. if other.is_extended_nonpositive:
  425. return S.true
  426. # Check if other >= 1
  427. res = self._value_one_or_more(other)
  428. if res is not None:
  429. return not(res)
  430. return Ge(self, other, evaluate=False)
  431. def __gt__(self, other):
  432. if self.is_extended_real:
  433. other = _sympify(other)
  434. # Check if other < 0
  435. res = self._value_one_or_more(other)
  436. if res is not None:
  437. return not(res)
  438. # Check if other >= 1
  439. if other.is_extended_negative:
  440. return S.true
  441. return Gt(self, other, evaluate=False)
  442. def __le__(self, other):
  443. if self.is_extended_real:
  444. other = _sympify(other)
  445. # Check if other < 0
  446. if other.is_extended_negative:
  447. return S.false
  448. # Check if other >= 1
  449. res = self._value_one_or_more(other)
  450. if res is not None:
  451. return res
  452. return Le(self, other, evaluate=False)
  453. def __lt__(self, other):
  454. if self.is_extended_real:
  455. other = _sympify(other)
  456. # Check if other <= 0
  457. if other.is_extended_nonpositive:
  458. return S.false
  459. # Check if other >= 1
  460. res = self._value_one_or_more(other)
  461. if res is not None:
  462. return res
  463. return Lt(self, other, evaluate=False)
  464. def _value_one_or_more(self, other):
  465. if other.is_extended_real:
  466. if other.is_number:
  467. res = other >= 1
  468. if res and not isinstance(res, Relational):
  469. return S.true
  470. if other.is_integer and other.is_positive:
  471. return S.true
  472. def _eval_as_leading_term(self, x, logx=None, cdir=0):
  473. from sympy.calculus.accumulationbounds import AccumBounds
  474. arg = self.args[0]
  475. arg0 = arg.subs(x, 0)
  476. r = self.subs(x, 0)
  477. if arg0.is_finite:
  478. if r.is_zero:
  479. ndir = arg.dir(x, cdir=cdir)
  480. if ndir.is_negative:
  481. return S.One
  482. return (arg - arg0).as_leading_term(x, logx=logx, cdir=cdir)
  483. else:
  484. return r
  485. elif arg0 in (S.ComplexInfinity, S.Infinity, S.NegativeInfinity):
  486. return AccumBounds(0, 1)
  487. return arg.as_leading_term(x, logx=logx, cdir=cdir)
  488. def _eval_nseries(self, x, n, logx, cdir=0):
  489. from sympy.series.order import Order
  490. arg = self.args[0]
  491. arg0 = arg.subs(x, 0)
  492. r = self.subs(x, 0)
  493. if arg0.is_infinite:
  494. from sympy.calculus.accumulationbounds import AccumBounds
  495. o = Order(1, (x, 0)) if n <= 0 else AccumBounds(0, 1) + Order(x**n, (x, 0))
  496. return o
  497. else:
  498. res = (arg - arg0)._eval_nseries(x, n, logx=logx, cdir=cdir)
  499. if r.is_zero:
  500. ndir = arg.dir(x, cdir=cdir)
  501. res += S.One if ndir.is_negative else S.Zero
  502. else:
  503. res += r
  504. return res
  505. @dispatch(frac, Basic) # type:ignore
  506. def _eval_is_eq(lhs, rhs): # noqa:F811
  507. if (lhs.rewrite(floor) == rhs) or \
  508. (lhs.rewrite(ceiling) == rhs):
  509. return True
  510. # Check if other < 0
  511. if rhs.is_extended_negative:
  512. return False
  513. # Check if other >= 1
  514. res = lhs._value_one_or_more(rhs)
  515. if res is not None:
  516. return False