# Source code for sympy.assumptions.handlers.calculus

"""
This module contains query handlers responsible for calculus queries:
infinitesimal, bounded, etc.
"""
from __future__ import print_function, division

from sympy.logic.boolalg import conjuncts
from sympy.assumptions.handlers import CommonHandler

"""
Handler for key 'infinitesimal'
Test that a given expression is equivalent to an infinitesimal
number
"""

@staticmethod
def _number(expr, assumptions):
# helper method
return expr.evalf() == 0

@staticmethod
def Basic(expr, assumptions):
if expr.is_number:

@staticmethod
[docs]    def Mul(expr, assumptions):
"""
Infinitesimal*Bounded -> Infinitesimal
"""
if expr.is_number:
result = False
for arg in expr.args:
result = True
continue
else:
break
else:
return result

@staticmethod
def Number(expr, assumptions):
return expr == 0

NumberSymbol = Number

ImaginaryUnit = staticmethod(CommonHandler.AlwaysFalse)

"""
Handler for key 'bounded'.

Test that an expression is bounded respect to all its variables.

Examples of usage:

>>> from sympy import Symbol, Q
>>> from sympy.abc import x
>>> a.Symbol(x, Q.positive(x)) == None
True
>>> a.Symbol(x, Q.bounded(x))
True

"""

@staticmethod
[docs]    def Symbol(expr, assumptions):
"""
Handles Symbol.

Examples:

>>> from sympy import Symbol, Q
>>> from sympy.abc import x
>>> a.Symbol(x, Q.positive(x)) == None
True
>>> a.Symbol(x, Q.bounded(x))
True

"""
if Q.bounded(expr) in conjuncts(assumptions):
return True
return None

@staticmethod
"""
Return True if expr is bounded, False if not and None if unknown.

Truth Table:

+-------+-----+-----------+-----------+
|       |     |           |           |
|       |  B  |     U     |     ?     |
|       |     |           |           |
+-------+-----+---+---+---+---+---+---+
|       |     |   |   |   |   |   |   |
|       |     |'+'|'-'|'x'|'+'|'-'|'x'|
|       |     |   |   |   |   |   |   |
+-------+-----+---+---+---+---+---+---+
|       |     |           |           |
|   B   |  B  |     U     |     ?     |
|       |     |           |           |
+---+---+-----+---+---+---+---+---+---+
|   |   |     |   |   |   |   |   |   |
|   |'+'|     | U | ? | ? | U | ? | ? |
|   |   |     |   |   |   |   |   |   |
|   +---+-----+---+---+---+---+---+---+
|   |   |     |   |   |   |   |   |   |
| U |'-'|     | ? | U | ? | ? | U | ? |
|   |   |     |   |   |   |   |   |   |
|   +---+-----+---+---+---+---+---+---+
|   |   |     |           |           |
|   |'x'|     |     ?     |     ?     |
|   |   |     |           |           |
+---+---+-----+---+---+---+---+---+---+
|       |     |           |           |
|   ?   |     |           |     ?     |
|       |     |           |           |
+-------+-----+-----------+---+---+---+

* 'B' = Bounded

* 'U' = Unbounded

* '?' = unknown boundedness

* '+' = positive sign

* '-' = negative sign

* 'x' = sign unknown

|

* All Bounded -> True

* 1 Unbounded and the rest Bounded -> False

* >1 Unbounded, all with same known sign -> False

* Any Unknown and unknown sign -> None

* Else -> None

When the signs are not the same you can have an undefined
result as in oo - oo, hence 'bounded' is also undefined.

"""

sign = -1  # sign of unknown or unbounded
result = True
for arg in expr.args:
if _bounded:
continue
# if there has been more than one sign or if the sign of this arg
# is None and Bounded is None or there was already
# an unknown sign, return None
if sign != -1 and s != sign or \
s is None and (s == _bounded or s == sign):
return None
else:
sign = s
# once False, do not change
if result is not False:
result = _bounded
return result

@staticmethod
[docs]    def Mul(expr, assumptions):
"""
Return True if expr is bounded, False if not and None if unknown.

Truth Table:

+---+---+---+--------+
|   |   |   |        |
|   | B | U |   ?    |
|   |   |   |        |
+---+---+---+---+----+
|   |   |   |   |    |
|   |   |   | s | /s |
|   |   |   |   |    |
+---+---+---+---+----+
|   |   |   |        |
| B | B | U |   ?    |
|   |   |   |        |
+---+---+---+---+----+
|   |   |   |   |    |
| U |   | U | U | ?  |
|   |   |   |   |    |
+---+---+---+---+----+
|   |   |   |        |
| ? |   |   |   ?    |
|   |   |   |        |
+---+---+---+---+----+

* B = Bounded

* U = Unbounded

* ? = unknown boundedness

* s = signed (hence nonzero)

* /s = not signed

"""
result = True
for arg in expr.args:
if _bounded:
continue
elif _bounded is None:
if result is None:
return None
return None
if result is not False:
result = None
else:
result = False
return result

@staticmethod
[docs]    def Pow(expr, assumptions):
"""
Unbounded ** NonZero -> Unbounded
Bounded ** Bounded -> Bounded
Abs()<=1 ** Positive -> Bounded
Abs()>=1 ** Negative -> Bounded
Otherwise unknown
"""
if base_bounded is None and exp_bounded is None:  # Common Case
return None
if base_bounded is False and ask(Q.nonzero(expr.exp), assumptions):
return False
if base_bounded and exp_bounded:
return True
if (abs(expr.base) <= 1) is True and ask(Q.positive(expr.exp), assumptions):
return True
if (abs(expr.base) >= 1) is True and ask(Q.negative(expr.exp), assumptions):
return True
if (abs(expr.base) >= 1) is True and exp_bounded is False:
return False
return None

@staticmethod
def log(expr, assumptions):