123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- from typing import Tuple as tTuple
- from sympy.core.basic import Basic
- from sympy.core.expr import Expr
- from sympy.core import Add, S
- from sympy.core.evalf import get_integer_part, PrecisionExhausted
- from sympy.core.function import Function
- from sympy.core.logic import fuzzy_or
- from sympy.core.numbers import Integer
- from sympy.core.relational import Gt, Lt, Ge, Le, Relational, is_eq
- from sympy.core.symbol import Symbol
- from sympy.core.sympify import _sympify
- from sympy.multipledispatch import dispatch
- ###############################################################################
- ######################### FLOOR and CEILING FUNCTIONS #########################
- ###############################################################################
- class RoundFunction(Function):
- """Abstract base class for rounding functions."""
- args: tTuple[Expr]
- @classmethod
- def eval(cls, arg):
- from sympy.functions.elementary.complexes import im
- v = cls._eval_number(arg)
- if v is not None:
- return v
- if arg.is_integer or arg.is_finite is False:
- return arg
- if arg.is_imaginary or (S.ImaginaryUnit*arg).is_real:
- i = im(arg)
- if not i.has(S.ImaginaryUnit):
- return cls(i)*S.ImaginaryUnit
- return cls(arg, evaluate=False)
- # Integral, numerical, symbolic part
- ipart = npart = spart = S.Zero
- # Extract integral (or complex integral) terms
- terms = Add.make_args(arg)
- for t in terms:
- if t.is_integer or (t.is_imaginary and im(t).is_integer):
- ipart += t
- elif t.has(Symbol):
- spart += t
- else:
- npart += t
- if not (npart or spart):
- return ipart
- # Evaluate npart numerically if independent of spart
- if npart and (
- not spart or
- npart.is_real and (spart.is_imaginary or (S.ImaginaryUnit*spart).is_real) or
- npart.is_imaginary and spart.is_real):
- try:
- r, i = get_integer_part(
- npart, cls._dir, {}, return_ints=True)
- ipart += Integer(r) + Integer(i)*S.ImaginaryUnit
- npart = S.Zero
- except (PrecisionExhausted, NotImplementedError):
- pass
- spart += npart
- if not spart:
- return ipart
- elif spart.is_imaginary or (S.ImaginaryUnit*spart).is_real:
- return ipart + cls(im(spart), evaluate=False)*S.ImaginaryUnit
- elif isinstance(spart, (floor, ceiling)):
- return ipart + spart
- else:
- return ipart + cls(spart, evaluate=False)
- @classmethod
- def _eval_number(cls, arg):
- raise NotImplementedError()
- def _eval_is_finite(self):
- return self.args[0].is_finite
- def _eval_is_real(self):
- return self.args[0].is_real
- def _eval_is_integer(self):
- return self.args[0].is_real
- class floor(RoundFunction):
- """
- Floor is a univariate function which returns the largest integer
- value not greater than its argument. This implementation
- generalizes floor to complex numbers by taking the floor of the
- real and imaginary parts separately.
- Examples
- ========
- >>> from sympy import floor, E, I, S, Float, Rational
- >>> floor(17)
- 17
- >>> floor(Rational(23, 10))
- 2
- >>> floor(2*E)
- 5
- >>> floor(-Float(0.567))
- -1
- >>> floor(-I/2)
- -I
- >>> floor(S(5)/2 + 5*I/2)
- 2 + 2*I
- See Also
- ========
- sympy.functions.elementary.integers.ceiling
- References
- ==========
- .. [1] "Concrete mathematics" by Graham, pp. 87
- .. [2] http://mathworld.wolfram.com/FloorFunction.html
- """
- _dir = -1
- @classmethod
- def _eval_number(cls, arg):
- if arg.is_Number:
- return arg.floor()
- elif any(isinstance(i, j)
- for i in (arg, -arg) for j in (floor, ceiling)):
- return arg
- if arg.is_NumberSymbol:
- return arg.approximation_interval(Integer)[0]
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- arg = self.args[0]
- arg0 = arg.subs(x, 0)
- r = self.subs(x, 0)
- if arg0.is_finite:
- if arg0 == r:
- if cdir == 0:
- ndirl = arg.dir(x, cdir=-1)
- ndir = arg.dir(x, cdir=1)
- if ndir != ndirl:
- raise ValueError("Two sided limit of %s around 0"
- "does not exist" % self)
- else:
- ndir = arg.dir(x, cdir=cdir)
- return r - 1 if ndir.is_negative else r
- else:
- return r
- return arg.as_leading_term(x, logx=logx, cdir=cdir)
- def _eval_nseries(self, x, n, logx, cdir=0):
- arg = self.args[0]
- arg0 = arg.subs(x, 0)
- if arg0.is_infinite:
- from sympy.calculus.accumulationbounds import AccumBounds
- from sympy.series.order import Order
- s = arg._eval_nseries(x, n, logx, cdir)
- o = Order(1, (x, 0)) if n <= 0 else AccumBounds(-1, 0)
- return s + o
- r = self.subs(x, 0)
- if arg0 == r:
- ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
- return r - 1 if ndir.is_negative else r
- else:
- return r
- def _eval_is_negative(self):
- return self.args[0].is_negative
- def _eval_is_nonnegative(self):
- return self.args[0].is_nonnegative
- def _eval_rewrite_as_ceiling(self, arg, **kwargs):
- return -ceiling(-arg)
- def _eval_rewrite_as_frac(self, arg, **kwargs):
- return arg - frac(arg)
- def __le__(self, other):
- other = S(other)
- if self.args[0].is_real:
- if other.is_integer:
- return self.args[0] < other + 1
- if other.is_number and other.is_real:
- return self.args[0] < ceiling(other)
- if self.args[0] == other and other.is_real:
- return S.true
- if other is S.Infinity and self.is_finite:
- return S.true
- return Le(self, other, evaluate=False)
- def __ge__(self, other):
- other = S(other)
- if self.args[0].is_real:
- if other.is_integer:
- return self.args[0] >= other
- if other.is_number and other.is_real:
- return self.args[0] >= ceiling(other)
- if self.args[0] == other and other.is_real:
- return S.false
- if other is S.NegativeInfinity and self.is_finite:
- return S.true
- return Ge(self, other, evaluate=False)
- def __gt__(self, other):
- other = S(other)
- if self.args[0].is_real:
- if other.is_integer:
- return self.args[0] >= other + 1
- if other.is_number and other.is_real:
- return self.args[0] >= ceiling(other)
- if self.args[0] == other and other.is_real:
- return S.false
- if other is S.NegativeInfinity and self.is_finite:
- return S.true
- return Gt(self, other, evaluate=False)
- def __lt__(self, other):
- other = S(other)
- if self.args[0].is_real:
- if other.is_integer:
- return self.args[0] < other
- if other.is_number and other.is_real:
- return self.args[0] < ceiling(other)
- if self.args[0] == other and other.is_real:
- return S.false
- if other is S.Infinity and self.is_finite:
- return S.true
- return Lt(self, other, evaluate=False)
- @dispatch(floor, Expr)
- def _eval_is_eq(lhs, rhs): # noqa:F811
- return is_eq(lhs.rewrite(ceiling), rhs) or \
- is_eq(lhs.rewrite(frac),rhs)
- class ceiling(RoundFunction):
- """
- Ceiling is a univariate function which returns the smallest integer
- value not less than its argument. This implementation
- generalizes ceiling to complex numbers by taking the ceiling of the
- real and imaginary parts separately.
- Examples
- ========
- >>> from sympy import ceiling, E, I, S, Float, Rational
- >>> ceiling(17)
- 17
- >>> ceiling(Rational(23, 10))
- 3
- >>> ceiling(2*E)
- 6
- >>> ceiling(-Float(0.567))
- 0
- >>> ceiling(I/2)
- I
- >>> ceiling(S(5)/2 + 5*I/2)
- 3 + 3*I
- See Also
- ========
- sympy.functions.elementary.integers.floor
- References
- ==========
- .. [1] "Concrete mathematics" by Graham, pp. 87
- .. [2] http://mathworld.wolfram.com/CeilingFunction.html
- """
- _dir = 1
- @classmethod
- def _eval_number(cls, arg):
- if arg.is_Number:
- return arg.ceiling()
- elif any(isinstance(i, j)
- for i in (arg, -arg) for j in (floor, ceiling)):
- return arg
- if arg.is_NumberSymbol:
- return arg.approximation_interval(Integer)[1]
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- arg = self.args[0]
- arg0 = arg.subs(x, 0)
- r = self.subs(x, 0)
- if arg0.is_finite:
- if arg0 == r:
- if cdir == 0:
- ndirl = arg.dir(x, cdir=-1)
- ndir = arg.dir(x, cdir=1)
- if ndir != ndirl:
- raise ValueError("Two sided limit of %s around 0"
- "does not exist" % self)
- else:
- ndir = arg.dir(x, cdir=cdir)
- return r if ndir.is_negative else r + 1
- else:
- return r
- return arg.as_leading_term(x, logx=logx, cdir=cdir)
- def _eval_nseries(self, x, n, logx, cdir=0):
- arg = self.args[0]
- arg0 = arg.subs(x, 0)
- if arg0.is_infinite:
- from sympy.calculus.accumulationbounds import AccumBounds
- from sympy.series.order import Order
- s = arg._eval_nseries(x, n, logx, cdir)
- o = Order(1, (x, 0)) if n <= 0 else AccumBounds(0, 1)
- return s + o
- r = self.subs(x, 0)
- if arg0 == r:
- ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
- return r if ndir.is_negative else r + 1
- else:
- return r
- def _eval_rewrite_as_floor(self, arg, **kwargs):
- return -floor(-arg)
- def _eval_rewrite_as_frac(self, arg, **kwargs):
- return arg + frac(-arg)
- def _eval_is_positive(self):
- return self.args[0].is_positive
- def _eval_is_nonpositive(self):
- return self.args[0].is_nonpositive
- def __lt__(self, other):
- other = S(other)
- if self.args[0].is_real:
- if other.is_integer:
- return self.args[0] <= other - 1
- if other.is_number and other.is_real:
- return self.args[0] <= floor(other)
- if self.args[0] == other and other.is_real:
- return S.false
- if other is S.Infinity and self.is_finite:
- return S.true
- return Lt(self, other, evaluate=False)
- def __gt__(self, other):
- other = S(other)
- if self.args[0].is_real:
- if other.is_integer:
- return self.args[0] > other
- if other.is_number and other.is_real:
- return self.args[0] > floor(other)
- if self.args[0] == other and other.is_real:
- return S.false
- if other is S.NegativeInfinity and self.is_finite:
- return S.true
- return Gt(self, other, evaluate=False)
- def __ge__(self, other):
- other = S(other)
- if self.args[0].is_real:
- if other.is_integer:
- return self.args[0] > other - 1
- if other.is_number and other.is_real:
- return self.args[0] > floor(other)
- if self.args[0] == other and other.is_real:
- return S.true
- if other is S.NegativeInfinity and self.is_finite:
- return S.true
- return Ge(self, other, evaluate=False)
- def __le__(self, other):
- other = S(other)
- if self.args[0].is_real:
- if other.is_integer:
- return self.args[0] <= other
- if other.is_number and other.is_real:
- return self.args[0] <= floor(other)
- if self.args[0] == other and other.is_real:
- return S.false
- if other is S.Infinity and self.is_finite:
- return S.true
- return Le(self, other, evaluate=False)
- @dispatch(ceiling, Basic) # type:ignore
- def _eval_is_eq(lhs, rhs): # noqa:F811
- return is_eq(lhs.rewrite(floor), rhs) or is_eq(lhs.rewrite(frac),rhs)
- class frac(Function):
- r"""Represents the fractional part of x
- For real numbers it is defined [1]_ as
- .. math::
- x - \left\lfloor{x}\right\rfloor
- Examples
- ========
- >>> from sympy import Symbol, frac, Rational, floor, I
- >>> frac(Rational(4, 3))
- 1/3
- >>> frac(-Rational(4, 3))
- 2/3
- returns zero for integer arguments
- >>> n = Symbol('n', integer=True)
- >>> frac(n)
- 0
- rewrite as floor
- >>> x = Symbol('x')
- >>> frac(x).rewrite(floor)
- x - floor(x)
- for complex arguments
- >>> r = Symbol('r', real=True)
- >>> t = Symbol('t', real=True)
- >>> frac(t + I*r)
- I*frac(r) + frac(t)
- See Also
- ========
- sympy.functions.elementary.integers.floor
- sympy.functions.elementary.integers.ceiling
- References
- ===========
- .. [1] https://en.wikipedia.org/wiki/Fractional_part
- .. [2] http://mathworld.wolfram.com/FractionalPart.html
- """
- @classmethod
- def eval(cls, arg):
- from sympy.calculus.accumulationbounds import AccumBounds
- from sympy.functions.elementary.complexes import im
- def _eval(arg):
- if arg in (S.Infinity, S.NegativeInfinity):
- return AccumBounds(0, 1)
- if arg.is_integer:
- return S.Zero
- if arg.is_number:
- if arg is S.NaN:
- return S.NaN
- elif arg is S.ComplexInfinity:
- return S.NaN
- else:
- return arg - floor(arg)
- return cls(arg, evaluate=False)
- terms = Add.make_args(arg)
- real, imag = S.Zero, S.Zero
- for t in terms:
- # Two checks are needed for complex arguments
- # see issue-7649 for details
- if t.is_imaginary or (S.ImaginaryUnit*t).is_real:
- i = im(t)
- if not i.has(S.ImaginaryUnit):
- imag += i
- else:
- real += t
- else:
- real += t
- real = _eval(real)
- imag = _eval(imag)
- return real + S.ImaginaryUnit*imag
- def _eval_rewrite_as_floor(self, arg, **kwargs):
- return arg - floor(arg)
- def _eval_rewrite_as_ceiling(self, arg, **kwargs):
- return arg + ceiling(-arg)
- def _eval_is_finite(self):
- return True
- def _eval_is_real(self):
- return self.args[0].is_extended_real
- def _eval_is_imaginary(self):
- return self.args[0].is_imaginary
- def _eval_is_integer(self):
- return self.args[0].is_integer
- def _eval_is_zero(self):
- return fuzzy_or([self.args[0].is_zero, self.args[0].is_integer])
- def _eval_is_negative(self):
- return False
- def __ge__(self, other):
- if self.is_extended_real:
- other = _sympify(other)
- # Check if other <= 0
- if other.is_extended_nonpositive:
- return S.true
- # Check if other >= 1
- res = self._value_one_or_more(other)
- if res is not None:
- return not(res)
- return Ge(self, other, evaluate=False)
- def __gt__(self, other):
- if self.is_extended_real:
- other = _sympify(other)
- # Check if other < 0
- res = self._value_one_or_more(other)
- if res is not None:
- return not(res)
- # Check if other >= 1
- if other.is_extended_negative:
- return S.true
- return Gt(self, other, evaluate=False)
- def __le__(self, other):
- if self.is_extended_real:
- other = _sympify(other)
- # Check if other < 0
- if other.is_extended_negative:
- return S.false
- # Check if other >= 1
- res = self._value_one_or_more(other)
- if res is not None:
- return res
- return Le(self, other, evaluate=False)
- def __lt__(self, other):
- if self.is_extended_real:
- other = _sympify(other)
- # Check if other <= 0
- if other.is_extended_nonpositive:
- return S.false
- # Check if other >= 1
- res = self._value_one_or_more(other)
- if res is not None:
- return res
- return Lt(self, other, evaluate=False)
- def _value_one_or_more(self, other):
- if other.is_extended_real:
- if other.is_number:
- res = other >= 1
- if res and not isinstance(res, Relational):
- return S.true
- if other.is_integer and other.is_positive:
- return S.true
- @dispatch(frac, Basic) # type:ignore
- def _eval_is_eq(lhs, rhs): # noqa:F811
- if (lhs.rewrite(floor) == rhs) or \
- (lhs.rewrite(ceiling) == rhs):
- return True
- # Check if other < 0
- if rhs.is_extended_negative:
- return False
- # Check if other >= 1
- res = lhs._value_one_or_more(rhs)
- if res is not None:
- return False
|