'Wrapper to _eval_nseries if assumptions allow, else to series.
If x is given, x0 is 0, dir=\'+\', and self has x, then _eval_nseries is
called. This calculates "n" terms in the innermost expressions and
then builds up the final series just by "cross-multiplying" everything
out.
The optional ``logx`` parameter can be used to replace any log(x) in the
returned series with a symbolic value to avoid evaluating log(x) at 0. A
symbol to use in place of log(x) should be provided.
Advantage -- it\'s fast, because we don\'t have to determine how many
terms we need to calculate in advance.
Disadvantage -- you may end up with less terms than you may have
expected, but the O(x**n) term appended will always be correct and
so the result, though perhaps shorter, will also be correct.
If any of those assumptions is not met, this is treated like a
wrapper to series which will try harder to return the correct
number of terms.
See also lseries().
Examples
>>> from sympy import sin, log, Symbol
>>> from sympy.abc import x, y
>>> sin(x).nseries(x, 0, 6)
x - x**3/6 + x**5/120 + O(x**6)
>>> log(x+1).nseries(x, 0, 5)
x - x**2/2 + x**3/3 - x**4/4 + O(x**5)
Handling of the ``logx`` parameter --- in the following example the
expansion fails since ``sin`` does not have an asymptotic expansion
at -oo (the limit of log(x) as x approaches 0):
>>> e = sin(log(x))
>>> e.nseries(x, 0, 6)
Traceback (most recent call last):
PoleError: ...
>>> logx = Symbol(\'logx\')
>>> e.nseries(x, 0, 6, logx=logx)
sin(logx)
In the following example, the expansion works but gives only an Order term
unless the ``logx`` parameter is used:
>>> e = x**y
>>> e.nseries(x, 0, 2)
O(log(x)**2)
>>> e.nseries(x, 0, 2, logx=logx)
exp(logx*y)'

def nseries(self, x=None, x0=0, n=6, dir='+', logx=None):

    if (x and (not (x in self.free_symbols))):
        return self
    if ((x is None) or x0 or (dir != '+')):
        return self.series(x, x0, n, dir)
    else:
        return self._eval_nseries(x, n=n, logx=logx)
