Ejemplo n.º 1
0
    def _eval_nseries(self, x, n, logx):
        # NOTE Please see the comment at the beginning of this file, labelled
        #      IMPORTANT.
        from sympy import cancel
        if not logx:
            logx = log(x)
        if self.args[0] == x:
            return logx
        arg = self.args[0]
        k, l = Wild("k"), Wild("l")
        r = arg.match(k * x**l)
        if r is not None:
            #k = r.get(r, S.One)
            #l = r.get(l, S.Zero)
            k, l = r[k], r[l]
            if l != 0 and not l.has(x) and not k.has(x):
                r = log(k) + l * logx  # XXX true regardless of assumptions?
                return r

        # TODO new and probably slow
        s = self.args[0].nseries(x, n=n, logx=logx)
        while s.is_Order:
            n += 1
            s = self.args[0].nseries(x, n=n, logx=logx)
        a, b = s.leadterm(x)
        p = cancel(s / (a * x**b) - 1)
        g = None
        l = []
        for i in xrange(n + 2):
            g = log.taylor_term(i, p, g)
            g = g.nseries(x, n=n, logx=logx)
            l.append(g)
        return log(a) + b * logx + Add(*l) + C.Order(p**n, x)
Ejemplo n.º 2
0
    def _eval_as_leading_term(self, x):
        arg = self.args[0].as_leading_term(x)

        if x in arg.free_symbols and C.Order(1, x).contains(arg):
            return 2 * x / sqrt(pi)
        else:
            return self.func(arg)
Ejemplo n.º 3
0
    def _eval_as_leading_term(self, x):
        arg = self.args[0].as_leading_term(x)

        if x in arg.free_symbols and C.Order(1, x).contains(arg):
            return S.ImaginaryUnit * S.Pi / 2
        else:
            return self.func(arg)
Ejemplo n.º 4
0
    def _eval_as_leading_term(self, x):
        arg = self.args[0].as_leading_term(x)

        if C.Order(1, x).contains(arg):
            return arg
        else:
            return self.func(arg)
Ejemplo n.º 5
0
 def _eval_as_leading_term(self, x):
     n, z = [a.as_leading_term(x) for a in self.args]
     o = C.Order(z, x)
     if n == 0 and o.contains(1 / x):
         return o.getn() * log(x)
     else:
         return self.func(n, z)
Ejemplo n.º 6
0
 def _taylor(self, x, n):
     l = []
     g = None
     for i in xrange(n):
         g = self.taylor_term(i, self.args[0], g)
         g = g.nseries(x, n=n)
         l.append(g)
     return Add(*l) + C.Order(x**n, x)
Ejemplo n.º 7
0
 def _eval_aseries(self, n, args0, x, logx):
     if args0[0] != oo:
         return super(loggamma, self)._eval_aseries(n, args0, x, logx)
     z = self.args[0]
     m = min(n, C.ceiling((n + S(1)) / 2))
     r = log(z) * (z - S(1) / 2) - z + log(2 * pi) / 2
     l = [
         bernoulli(2 * k) / (2 * k * (2 * k - 1) * z**(2 * k - 1))
         for k in range(1, m)
     ]
     o = None
     if m == 0:
         o = C.Order(1, x)
     else:
         o = C.Order(1 / z**(2 * m - 1), x)
     # It is very inefficient to first add the order and then do the nseries
     return (r + Add(*l))._eval_nseries(x, n, logx) + o
Ejemplo n.º 8
0
 def _eval_as_leading_term(self, x):
     arg = self.args[0]
     if arg.is_Add:
         return Mul(*[exp(f).as_leading_term(x) for f in arg.args])
     arg = self.args[0].as_leading_term(x)
     if C.Order(1, x).contains(arg):
         return S.One
     return exp(arg)
Ejemplo n.º 9
0
    def doit(self, **hints):
        """Evaluates limit"""
        e, z, z0, dir = self.args

        if hints.get('deep', True):
            e = e.doit(**hints)
            z = z.doit(**hints)
            z0 = z0.doit(**hints)

        if e == z:
            return z0

        if not e.has(z):
            return e

        # gruntz fails on factorials but works with the gamma function
        # If no factorial term is present, e should remain unchanged.
        # factorial is defined to be zero for negative inputs (which
        # differs from gamma) so only rewrite for positive z0.
        if z0.is_positive:
            e = e.rewrite(factorial, gamma)

        if e.is_Mul:
            if abs(z0) is S.Infinity:
                # XXX todo: this should probably be stated in the
                # negative -- i.e. to exclude expressions that should
                # not be handled this way but I'm not sure what that
                # condition is; when ok is True it means that the leading
                # term approach is going to succeed (hopefully)
                ok = lambda w: (z in w.free_symbols and
                                any(a.is_polynomial(z) or
                                    any(z in m.free_symbols and m.is_polynomial(z)
                                        for m in Mul.make_args(a))
                                    for a in Add.make_args(w)))
                if all(ok(w) for w in e.as_numer_denom()):
                    u = C.Dummy(positive=(z0 is S.Infinity))
                    inve = e.subs(z, 1/u)
                    r = limit(inve.as_leading_term(u), u,
                              S.Zero, "+" if z0 is S.Infinity else "-")
                    if isinstance(r, Limit):
                        return self
                    else:
                        return r

        if e.is_Order:
            return C.Order(limit(e.expr, z, z0), *e.args[1:])

        try:
            r = gruntz(e, z, z0, dir)
            if r is S.NaN:
                raise PoleError()
        except (PoleError, ValueError):
            r = heuristics(e, z, z0, dir)
            if r is None:
                return self

        return r
Ejemplo n.º 10
0
    def _eval_aseries(self, n, args0, x, logx):
        if args0[1] != oo or not \
           (self.args[0].is_Integer and self.args[0].is_nonnegative):
            return super(polygamma, self)._eval_aseries(n, args0, x, logx)
        z = self.args[1]
        N = self.args[0]

        if N == 0:
            # digamma function series
            # Abramowitz & Stegun, p. 259, 6.3.18
            r = log(z) - 1 / (2 * z)
            o = None
            if n < 2:
                o = C.Order(1 / z, x)
            else:
                m = C.ceiling((n + 1) / 2)
                l = [
                    bernoulli(2 * k) / (2 * k * z**(2 * k))
                    for k in range(1, m)
                ]
                r -= Add(*l)
                o = C.Order(1 / z**(2 * m), x)
            return r._eval_nseries(x, n, logx) + o
        else:
            # proper polygamma function
            # Abramowitz & Stegun, p. 260, 6.4.10
            # We return terms to order higher than O(x**n) on purpose
            # -- otherwise we would not be able to return any terms for
            #    quite a long time!
            fac = gamma(N)
            e0 = fac + N * fac / (2 * z)
            m = C.ceiling((n + 1) / 2)
            for k in range(1, m):
                fac = fac * (2 * k + N - 1) * (2 * k + N - 2) / ((2 * k) *
                                                                 (2 * k - 1))
                e0 += bernoulli(2 * k) * fac / z**(2 * k)
            o = C.Order(1 / z**(2 * m), x)
            if n == 0:
                o = C.Order(1 / z, x)
            elif n == 1:
                o = C.Order(1 / z**2, x)
            r = e0._eval_nseries(z, n, logx) + o
            return -1 * (-1 / z)**N * r
Ejemplo n.º 11
0
    def _eval_aseries(self, n, args0, x, logx):
        if args0[0] != S.Infinity:
            return super(_erfs, self)._eval_aseries(n, args0, x, logx)

        z = self.args[0]
        l = [ 1/sqrt(S.Pi) * C.factorial(2*k)*(-S(
            4))**(-k)/C.factorial(k) * (1/z)**(2*k + 1) for k in xrange(0, n) ]
        o = C.Order(1/z**(2*n + 1), x)
        # It is very inefficient to first add the order and then do the nseries
        return (Add(*l))._eval_nseries(x, n, logx) + o
Ejemplo n.º 12
0
 def _eval_nseries(self, x, n, logx):
     # NOTE Please see the comment at the beginning of this file, labelled
     #      IMPORTANT.
     from sympy import limit, oo, powsimp
     arg = self.args[0]
     arg_series = arg._eval_nseries(x, n=n, logx=logx)
     if arg_series.is_Order:
         return 1 + arg_series
     arg0 = limit(arg_series.removeO(), x, 0)
     if arg0 in [-oo, oo]:
         return self
     t = Dummy("t")
     exp_series = exp(t)._taylor(t, n)
     o = exp_series.getO()
     exp_series = exp_series.removeO()
     r = exp(arg0)*exp_series.subs(t, arg_series - arg0)
     r += C.Order(o.expr.subs(t, (arg_series - arg0)), x)
     r = r.expand()
     return powsimp(r, deep=True, combine='exp')
Ejemplo n.º 13
0
def limit(e, z, z0, dir="+"):
    """
    Compute the limit of e(z) at the point z0.

    z0 can be any expression, including oo and -oo.

    For dir="+" (default) it calculates the limit from the right
    (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0
    (oo or -oo), the dir argument doesn't matter.

    Examples
    ========

    >>> from sympy import limit, sin, Symbol, oo
    >>> from sympy.abc import x
    >>> limit(sin(x)/x, x, 0)
    1
    >>> limit(1/x, x, 0, dir="+")
    oo
    >>> limit(1/x, x, 0, dir="-")
    -oo
    >>> limit(1/x, x, oo)
    0

    Notes
    =====

    First we try some heuristics for easy and frequent cases like "x", "1/x",
    "x**2" and similar, so that it's fast. For all other cases, we use the
    Gruntz algorithm (see the gruntz() function).
    """
    e = sympify(e)
    z = sympify(z)
    z0 = sympify(z0)

    if e == z:
        return z0

    if not e.has(z):
        return e

    # gruntz fails on factorials but works with the gamma function
    # If no factorial term is present, e should remain unchanged.
    # factorial is defined to be zero for negative inputs (which
    # differs from gamma) so only rewrite for positive z0.
    if z0.is_positive:
        e = e.rewrite(factorial, gamma)

    if e.is_Mul:
        if abs(z0) is S.Infinity:
            # XXX todo: this should probably be stated in the
            # negative -- i.e. to exclude expressions that should
            # not be handled this way but I'm not sure what that
            # condition is; when ok is True it means that the leading
            # term approach is going to succeed (hopefully)
            ok = lambda w: (z in w.free_symbols and any(
                a.is_polynomial(z) or any(
                    z in m.free_symbols and m.is_polynomial(z)
                    for m in Mul.make_args(a)) for a in Add.make_args(w)))
            if all(ok(w) for w in e.as_numer_denom()):
                u = C.Dummy(positive=(z0 is S.Infinity))
                inve = e.subs(z, 1 / u)
                return limit(inve.as_leading_term(u), u, S.Zero,
                             "+" if z0 is S.Infinity else "-")

    if e.is_Order:
        return C.Order(limit(e.expr, z, z0), *e.args[1:])

    try:
        r = gruntz(e, z, z0, dir)
        if r is S.NaN:
            raise PoleError()
    except (PoleError, ValueError):
        r = heuristics(e, z, z0, dir)
    return r
Ejemplo n.º 14
0
def limit(e, z, z0, dir="+"):
    """
    Compute the limit of e(z) at the point z0.

    z0 can be any expression, including oo and -oo.

    For dir="+" (default) it calculates the limit from the right
    (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0
    (oo or -oo), the dir argument doesn't matter.

    Examples
    ========

    >>> from sympy import limit, sin, Symbol, oo
    >>> from sympy.abc import x
    >>> limit(sin(x)/x, x, 0)
    1
    >>> limit(1/x, x, 0, dir="+")
    oo
    >>> limit(1/x, x, 0, dir="-")
    -oo
    >>> limit(1/x, x, oo)
    0

    Notes
    =====

    First we try some heuristics for easy and frequent cases like "x", "1/x",
    "x**2" and similar, so that it's fast. For all other cases, we use the
    Gruntz algorithm (see the gruntz() function).
    """
    from sympy import Wild, log

    e = sympify(e)
    z = sympify(z)
    z0 = sympify(z0)

    if e == z:
        return z0

    if e.is_Rational:
        return e

    if not e.has(z):
        return e

    # gruntz fails on factorials but works with the gamma function
    # If no factorial term is present, e should remain unchanged.
    # factorial is defined to be zero for negative inputs (which
    # differs from gamma) so only rewrite for positive z0.
    if z0.is_positive:
        e = e.rewrite(factorial, gamma)

    if e.func is tan:
        # discontinuity at odd multiples of pi/2; 0 at even
        disc = S.Pi/2
        sign = 1
        if dir == '-':
            sign *= -1
        i = limit(sign*e.args[0], z, z0)/disc
        if i.is_integer:
            if i.is_even:
                return S.Zero
            elif i.is_odd:
                if dir == '+':
                    return S.NegativeInfinity
                else:
                    return S.Infinity

    if e.func is cot:
        # discontinuity at multiples of pi; 0 at odd pi/2 multiples
        disc = S.Pi
        sign = 1
        if dir == '-':
            sign *= -1
        i = limit(sign*e.args[0], z, z0)/disc
        if i.is_integer:
            if dir == '-':
                return S.NegativeInfinity
            else:
                return S.Infinity
        elif (2*i).is_integer:
            return S.Zero

    if e.is_Pow:
        b, ex = e.args
        c = None  # records sign of b if b is +/-z or has a bounded value
        if b.is_Mul:
            c, b = b.as_two_terms()
            if c is S.NegativeOne and b == z:
                c = '-'
        elif b == z:
            c = '+'

        if ex.is_number:
            if c is None:
                base = b.subs(z, z0)
                if base != 0 and (ex.is_bounded or base is not S.One):
                    return base**ex
            else:
                if z0 == 0 and ex < 0:
                    if dir != c:
                        # integer
                        if ex.is_even:
                            return S.Infinity
                        elif ex.is_odd:
                            return S.NegativeInfinity
                        # rational
                        elif ex.is_Rational:
                            return (S.NegativeOne**ex)*S.Infinity
                        else:
                            return S.ComplexInfinity
                    return S.Infinity
                return z0**ex

    if e.is_Mul or not z0 and e.is_Pow and b.func is log:
        if e.is_Mul:
            if abs(z0) is S.Infinity:
                n, d = e.as_numer_denom()
                # XXX todo: this should probably be stated in the
                # negative -- i.e. to exclude expressions that should
                # not be handled this way but I'm not sure what that
                # condition is; when ok is True it means that the leading
                # term approach is going to succeed (hopefully)
                ok = lambda w: (z in w.free_symbols and
                     any(a.is_polynomial(z) or
                     any(z in m.free_symbols and m.is_polynomial(z)
                     for m in Mul.make_args(a))
                     for a in Add.make_args(w)))
                if all(ok(w) for w in (n, d)):
                    u = C.Dummy(positive=(z0 is S.Infinity))
                    inve = (n/d).subs(z, 1/u)
                    return limit(inve.as_leading_term(u), u,
                        S.Zero, "+" if z0 is S.Infinity else "-")

            # weed out the z-independent terms
            i, d = e.as_independent(z)
            if i is not S.One and i.is_bounded:
                return i*limit(d, z, z0, dir)
        else:
            i, d = S.One, e
        if not z0:
            # look for log(z)**q or z**p*log(z)**q
            p, q = Wild("p"), Wild("q")
            r = d.match(z**p * log(z)**q)
            if r:
                p, q = [r.get(w, w) for w in [p, q]]
                if q and q.is_number and p.is_number:
                    if q > 0:
                        if p > 0:
                            return S.Zero
                        else:
                            return -oo*i
                    else:
                        if p >= 0:
                            return S.Zero
                        else:
                            return -oo*i

    if e.is_Add:
        if e.is_polynomial():
            if not z0.is_unbounded:
                return Add(*[limit(term, z, z0, dir) for term in e.args])
        elif e.is_rational_function(z):
            rval = Add(*[limit(term, z, z0, dir) for term in e.args])
            if rval != S.NaN:
                return rval
        if not any([a.is_unbounded for a in e.args]):
            e = e.normal() # workaround for issue 3744

    if e.is_Order:
        args = e.args
        return C.Order(limit(args[0], z, z0), *args[1:])

    try:
        r = gruntz(e, z, z0, dir)
        if r is S.NaN:
            raise PoleError()
    except (PoleError, ValueError):
        r = heuristics(e, z, z0, dir)
    return r
Ejemplo n.º 15
0
 def _eval_nseries(self, x, x0, n):
     from sympy import powsimp
     arg = self.args[0]
     k, l = Wild("k"), Wild("l")
     r = arg.match(k * x**l)
     if r is not None:
         k, l = r[k], r[l]
         if l != 0 and not l.has(x) and not k.has(x):
             r = log(k) + l * log(x)
             return r
     order = C.Order(x**n, x)
     arg = self.args[0]
     x = order.symbols[0]
     ln = C.log
     use_lt = not C.Order(1, x).contains(arg)
     if not use_lt:
         arg0 = arg.limit(x, 0)
         use_lt = (arg0 is S.Zero)
     if use_lt:  # singularity, #example: self = log(sin(x))
         # arg = (arg / lt) * lt
         lt = arg.as_leading_term(x)  # arg = sin(x); lt = x
         a = powsimp((arg / lt).expand(), deep=True,
                     combine='exp')  # a = sin(x)/x
         # the idea is to recursively call ln(a).series(), but one needs to
         # make sure that ln(sin(x)/x) doesn't get "simplified" to
         # -log(x)+ln(sin(x)) and an infinite recursion occurs, see also the
         # issue 252.
         obj = ln(lt) + ln(a)._eval_nseries(x, x0, n)
     else:
         # arg -> arg0 + (arg - arg0) -> arg0 * (1 + (arg/arg0 - 1))
         z = (arg / arg0 - 1)
         x = order.symbols[0]
         ln = C.log
         o = C.Order(z, x)
         if o is S.Zero:
             return ln(1 + z) + ln(arg0)
         if o.expr.is_number:
             e = ln(order.expr * x) / ln(x)
         else:
             e = ln(order.expr) / ln(o.expr)
         n = e.limit(x, 0) + 1
         if n.is_unbounded:
             # requested accuracy gives infinite series,
             # order is probably nonpolynomial e.g. O(exp(-1/x), x).
             return ln(1 + z) + ln(arg0)
         try:
             n = int(n)
         except TypeError:
             #well, the n is something more complicated (like 1+log(2))
             n = int(n.evalf()) + 1
         assert n >= 0, ` n `
         l = []
         g = None
         for i in xrange(n + 2):
             g = ln.taylor_term(i, z, g)
             g = g.nseries(x, x0, n)
             l.append(g)
         obj = C.Add(*l) + ln(arg0)
     obj2 = expand_log(powsimp(obj, deep=True, combine='exp'))
     if obj2 != obj:
         r = obj2.nseries(x, x0, n)
     else:
         r = obj
     if r == self:
         return self
     return r + order
Ejemplo n.º 16
0
def limit(e, z, z0, dir="+"):
    """
    Compute the limit of e(z) at the point z0.

    z0 can be any expression, including oo and -oo.

    For dir="+" (default) it calculates the limit from the right
    (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0
    (oo or -oo), the dir argument doesn't matter.

    Examples
    ========

    >>> from sympy import limit, sin, Symbol, oo
    >>> from sympy.abc import x
    >>> limit(sin(x)/x, x, 0)
    1
    >>> limit(1/x, x, 0, dir="+")
    oo
    >>> limit(1/x, x, 0, dir="-")
    -oo
    >>> limit(1/x, x, oo)
    0

    Notes
    =====

    First we try some heuristics for easy and frequent cases like "x", "1/x",
    "x**2" and similar, so that it's fast. For all other cases, we use the
    Gruntz algorithm (see the gruntz() function).
    """
    from sympy import Wild, log

    e = sympify(e)
    z = sympify(z)
    z0 = sympify(z0)

    if e == z:
        return z0

    if e.is_Rational:
        return e

    if not e.has(z):
        return e

    if e.func is tan:
        # discontinuity at odd multiples of pi/2; 0 at even
        disc = S.Pi / 2
        sign = 1
        if dir == '-':
            sign *= -1
        i = limit(sign * e.args[0], z, z0) / disc
        if i.is_integer:
            if i.is_even:
                return S.Zero
            elif i.is_odd:
                if dir == '+':
                    return S.NegativeInfinity
                else:
                    return S.Infinity

    if e.func is cot:
        # discontinuity at multiples of pi; 0 at odd pi/2 multiples
        disc = S.Pi
        sign = 1
        if dir == '-':
            sign *= -1
        i = limit(sign * e.args[0], z, z0) / disc
        if i.is_integer:
            if dir == '-':
                return S.NegativeInfinity
            else:
                return S.Infinity
        elif (2 * i).is_integer:
            return S.Zero

    if e.is_Pow:
        b, ex = e.args
        c = None  # records sign of b if b is +/-z or has a bounded value
        if b.is_Mul:
            c, b = b.as_two_terms()
            if c is S.NegativeOne and b == z:
                c = '-'
        elif b == z:
            c = '+'

        if ex.is_number:
            if c is None:
                base = b.subs(z, z0)
                if base.is_bounded and (ex.is_bounded or base is not S.One):
                    return base**ex
            else:
                if z0 == 0 and ex < 0:
                    if dir != c:
                        # integer
                        if ex.is_even:
                            return S.Infinity
                        elif ex.is_odd:
                            return S.NegativeInfinity
                        # rational
                        elif ex.is_Rational:
                            return (S.NegativeOne**ex) * S.Infinity
                        else:
                            return S.ComplexInfinity
                    return S.Infinity
                return z0**ex

    if e.is_Mul or not z0 and e.is_Pow and b.func is log:
        if e.is_Mul:
            # weed out the z-independent terms
            i, d = e.as_independent(z)
            if i is not S.One and i.is_bounded:
                return i * limit(d, z, z0, dir)
        else:
            i, d = S.One, e
        if not z0:
            # look for log(z)**q or z**p*log(z)**q
            p, q = Wild("p"), Wild("q")
            r = d.match(z**p * log(z)**q)
            if r:
                p, q = [r.get(w, w) for w in [p, q]]
                if q and q.is_number and p.is_number:
                    if q > 0:
                        if p > 0:
                            return S.Zero
                        else:
                            return -oo * i
                    else:
                        if p >= 0:
                            return S.Zero
                        else:
                            return -oo * i

    if e.is_Add:
        if e.is_polynomial() and not z0.is_unbounded:
            return Add(*[limit(term, z, z0, dir) for term in e.args])

        # this is a case like limit(x*y+x*z, z, 2) == x*y+2*x
        # but we need to make sure, that the general gruntz() algorithm is
        # executed for a case like "limit(sqrt(x+1)-sqrt(x),x,oo)==0"
        unbounded = []
        unbounded_result = []
        finite = []
        unknown = []
        ok = True
        for term in e.args:
            if not term.has(z) and not term.is_unbounded:
                finite.append(term)
                continue
            result = term.subs(z, z0)
            bounded = result.is_bounded
            if bounded is False or result is S.NaN:
                if unknown:
                    ok = False
                    break
                unbounded.append(term)
                if result != S.NaN:
                    # take result from direction given
                    result = limit(term, z, z0, dir)
                unbounded_result.append(result)
            elif bounded:
                finite.append(result)
            else:
                if unbounded:
                    ok = False
                    break
                unknown.append(result)
        if not ok:
            # we won't be able to resolve this with unbounded
            # terms, e.g. Sum(1/k, (k, 1, n)) - log(n) as n -> oo:
            # since the Sum is unevaluated it's boundedness is
            # unknown and the log(n) is oo so you get Sum - oo
            # which is unsatisfactory.
            raise NotImplementedError('unknown boundedness for %s' %
                                      (unknown or result))
        u = Add(*unknown)
        if unbounded:
            inf_limit = Add(*unbounded_result)
            if inf_limit is not S.NaN:
                return inf_limit + u
            if finite:
                return Add(*finite) + limit(Add(*unbounded), z, z0, dir) + u
        else:
            return Add(*finite) + u

    if e.is_Order:
        args = e.args
        return C.Order(limit(args[0], z, z0), *args[1:])

    try:
        r = gruntz(e, z, z0, dir)
        if r is S.NaN:
            raise PoleError()
    except PoleError:
        r = heuristics(e, z, z0, dir)
    return r
Ejemplo n.º 17
0
def limit(e, z, z0, dir="+"):
    """
    Compute the limit of e(z) at the point z0.

    z0 can be any expression, including oo and -oo.

    For dir="+" (default) it calculates the limit from the right
    (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0
    (oo or -oo), the dir argument doesn't matter.

    Examples
    ========

    >>> from sympy import limit, sin, Symbol, oo
    >>> from sympy.abc import x
    >>> limit(sin(x)/x, x, 0)
    1
    >>> limit(1/x, x, 0, dir="+")
    oo
    >>> limit(1/x, x, 0, dir="-")
    -oo
    >>> limit(1/x, x, oo)
    0

    Notes
    =====

    First we try some heuristics for easy and frequent cases like "x", "1/x",
    "x**2" and similar, so that it's fast. For all other cases, we use the
    Gruntz algorithm (see the gruntz() function).
    """
    from sympy import Wild, log

    e = sympify(e)
    z = sympify(z)
    z0 = sympify(z0)

    if e == z:
        return z0

    if e.is_Rational:
        return e

    if not e.has(z):
        return e

    # gruntz fails on factorials but works with the gamma function
    # If no factorial term is present, e should remain unchanged.
    # factorial is defined to be zero for negative inputs (which
    # differs from gamma) so only rewrite for positive z0.
    if z0.is_positive:
        e = e.rewrite(factorial, gamma)

    if e.func is tan:
        # discontinuity at odd multiples of pi/2; 0 at even
        disc = S.Pi / 2
        sign = 1
        if dir == '-':
            sign *= -1
        i = limit(sign * e.args[0], z, z0) / disc
        if i.is_integer:
            if i.is_even:
                return S.Zero
            elif i.is_odd:
                if dir == '+':
                    return S.NegativeInfinity
                else:
                    return S.Infinity

    if e.func is cot:
        # discontinuity at multiples of pi; 0 at odd pi/2 multiples
        disc = S.Pi
        sign = 1
        if dir == '-':
            sign *= -1
        i = limit(sign * e.args[0], z, z0) / disc
        if i.is_integer:
            if dir == '-':
                return S.NegativeInfinity
            else:
                return S.Infinity
        elif (2 * i).is_integer:
            return S.Zero

    if e.is_Pow:
        b, ex = e.args
        c = None  # records sign of b if b is +/-z or has a bounded value
        if b.is_Mul:
            c, b = b.as_two_terms()
            if c is S.NegativeOne and b == z:
                c = '-'
        elif b == z:
            c = '+'

        if ex.is_number:
            if c is None:
                base = b.subs(z, z0)
                if base.is_finite and (ex.is_bounded or base is not S.One):
                    return base**ex
            else:
                if z0 == 0 and ex < 0:
                    if dir != c:
                        # integer
                        if ex.is_even:
                            return S.Infinity
                        elif ex.is_odd:
                            return S.NegativeInfinity
                        # rational
                        elif ex.is_Rational:
                            return (S.NegativeOne**ex) * S.Infinity
                        else:
                            return S.ComplexInfinity
                    return S.Infinity
                return z0**ex

    if e.is_Mul or not z0 and e.is_Pow and b.func is log:
        if e.is_Mul:
            if abs(z0) is S.Infinity:
                n, d = e.as_numer_denom()
                # XXX todo: this should probably be stated in the
                # negative -- i.e. to exclude expressions that should
                # not be handled this way but I'm not sure what that
                # condition is; when ok is True it means that the leading
                # term approach is going to succeed (hopefully)
                ok = lambda w: (z in w.free_symbols and any(
                    a.is_polynomial(z) or any(
                        z in m.free_symbols and m.is_polynomial(z)
                        for m in Mul.make_args(a)) for a in Add.make_args(w)))
                if all(ok(w) for w in (n, d)):
                    u = C.Dummy(positive=(z0 is S.Infinity))
                    inve = (n / d).subs(z, 1 / u)
                    return limit(inve.as_leading_term(u), u, S.Zero,
                                 "+" if z0 is S.Infinity else "-")

            # weed out the z-independent terms
            i, d = e.as_independent(z)
            if i is not S.One and i.is_bounded:
                return i * limit(d, z, z0, dir)
        else:
            i, d = S.One, e
        if not z0:
            # look for log(z)**q or z**p*log(z)**q
            p, q = Wild("p"), Wild("q")
            r = d.match(z**p * log(z)**q)
            if r:
                p, q = [r.get(w, w) for w in [p, q]]
                if q and q.is_number and p.is_number:
                    if q > 0:
                        if p > 0:
                            return S.Zero
                        else:
                            return -oo * i
                    else:
                        if p >= 0:
                            return S.Zero
                        else:
                            return -oo * i

    if e.is_Add:
        if e.is_polynomial() and not z0.is_unbounded:
            return Add(*[limit(term, z, z0, dir) for term in e.args])

        # this is a case like limit(x*y+x*z, z, 2) == x*y+2*x
        # but we need to make sure, that the general gruntz() algorithm is
        # executed for a case like "limit(sqrt(x+1)-sqrt(x),x,oo)==0"

        unbounded = []
        unbounded_result = []
        unbounded_const = []
        unknown = []
        unknown_result = []
        finite = []
        zero = []

        def _sift(term):
            if z not in term.free_symbols:
                if term.is_unbounded:
                    unbounded_const.append(term)
                else:
                    finite.append(term)
            else:
                result = term.subs(z, z0)
                bounded = result.is_bounded
                if bounded is False or result is S.NaN:
                    unbounded.append(term)
                    if result != S.NaN:
                        # take result from direction given
                        result = limit(term, z, z0, dir)
                    unbounded_result.append(result)
                elif bounded:
                    if result:
                        finite.append(result)
                    else:
                        zero.append(term)
                else:
                    unknown.append(term)
                    unknown_result.append(result)

        for term in e.args:
            _sift(term)

        bad = bool(unknown and unbounded)
        if bad or len(unknown) > 1 or len(unbounded) > 1 and not zero:
            uu = unknown + unbounded
            # we won't be able to resolve this with unbounded
            # terms, e.g. Sum(1/k, (k, 1, n)) - log(n) as n -> oo:
            # since the Sum is unevaluated it's boundedness is
            # unknown and the log(n) is oo so you get Sum - oo
            # which is unsatisfactory. BUT...if there are both
            # unknown and unbounded terms (condition 'bad') or
            # there are multiple terms that are unknown, or
            # there are multiple symbolic unbounded terms they may
            # respond better if they are made into a rational
            # function, so give them a chance to do so before
            # reporting failure.
            u = Add(*uu)
            f = u.normal()
            if f != u:
                unknown = []
                unbounded = []
                unbounded_result = []
                unknown_result = []
                _sift(limit(f, z, z0, dir))

            # We came in with a) unknown and unbounded terms or b) had multiple
            # unknown terms

            # At this point we've done one of 3 things.
            # (1) We did nothing with f so we now report the error
            # showing the troublesome terms which are now in uu. OR

            # (2) We did something with f but the result came back as unknown.
            # Normally this wouldn't be a problem,
            # but we had either multiple terms that were troublesome (unk and
            # unbounded or multiple unknown terms) so if we
            # weren't able to resolve the boundedness by now, that indicates a
            # problem so we report the error showing the troublesome terms which are
            # now in uu.
            if unknown:
                if bad:
                    msg = 'unknown and unbounded terms present in %s'
                elif unknown:
                    msg = 'multiple terms with unknown boundedness in %s'
                raise NotImplementedError(msg % uu)
            # OR
            # (3) the troublesome terms have been identified as finite or unbounded
            # and we proceed with the non-error code since the lists have been updated.

        u = Add(*unknown_result)
        if unbounded_result or unbounded_const:
            unbounded.extend(zero)
            inf_limit = Add(*(unbounded_result + unbounded_const))
            if inf_limit is not S.NaN:
                return inf_limit + u
            if finite:
                return Add(*finite) + limit(Add(*unbounded), z, z0, dir) + u
        else:
            return Add(*finite) + u

    if e.is_Order:
        args = e.args
        return C.Order(limit(args[0], z, z0), *args[1:])

    try:
        r = gruntz(e, z, z0, dir)
        if r is S.NaN:
            raise PoleError()
    except (PoleError, ValueError):
        r = heuristics(e, z, z0, dir)
    return r