Repository: sympy/sympy

Issue:
as_numer_denom() incorrect for Order term
```
>>> print(s)
(n**6/23040 - n**5/1536 + 17*n**4/4608 - 5*n**3/512 + 137*n**2/11520 - n/192)/N**5 + (-n**5/1920 + n**4/192 - 7*n**3/384 + 5*n**2/192 - n/80)/N**4 + (n**4/192 - n**3/32 + 11*n**2/192 - n/32)/N**3 + (-n**3/24 + n**2/8 - n/12)/N**2 + (n**2/4 - n/4)/N + O(N**(-6), (N, oo))
>>> s.as_numer_denom()
(O(N**9, (N, oo)), 815372697600*N**15)
```

This causes `s.simplify()` to remove everything except for the order term. 

Discussion:

Patch:
diff --git a/sympy/series/order.py b/sympy/series/order.py
index f0e29dd609..ddbd2988fe 100644
--- a/sympy/series/order.py
+++ b/sympy/series/order.py
@@ -179,7 +179,8 @@ def __new__(cls, expr, *args, **kwargs):
 
         if variables:
             if any(p != point[0] for p in point):
-                raise NotImplementedError
+                raise NotImplementedError(
+                    "Multivariable orders at different points are not supported.")
             if point[0] is S.Infinity:
                 s = {k: 1/Dummy() for k in variables}
                 rs = {1/v: 1/k for k, v in s.items()}
@@ -336,21 +337,11 @@ def contains(self, expr):
             return True
         if expr is S.NaN:
             return False
+        point = self.point[0] if self.point else S.Zero
         if expr.is_Order:
-            if (not all(p == expr.point[0] for p in expr.point) and
-                   not all(p == self.point[0] for p in self.point)):  # pragma: no cover
-                raise NotImplementedError('Order at points other than 0 '
-                    'or oo not supported, got %s as a point.' % point)
-            else:
-                # self and/or expr is O(1):
-                if any(not p for p in [expr.point, self.point]):
-                    point = self.point + expr.point
-                    if point:
-                        point = point[0]
-                    else:
-                        point = S.Zero
-                else:
-                    point = self.point[0]
+            if (any(p != point for p in expr.point) or
+                   any(p != point for p in self.point)):
+                return None
             if expr.expr == self.expr:
                 # O(1) + O(1), O(1) + O(1, x), etc.
                 return all([x in self.args[1:] for x in expr.args[1:]])
@@ -368,17 +359,19 @@ def contains(self, expr):
                 common_symbols = expr.variables
             if not common_symbols:
                 return None
-            if (self.expr.is_Pow and self.expr.base.is_symbol
-                and self.expr.exp.is_positive):
-                if expr.expr.is_Pow and self.expr.base == expr.expr.base:
-                    return not (self.expr.exp-expr.expr.exp).is_positive
-                if expr.expr.is_Mul:
-                    for arg in expr.expr.args:
-                        if (arg.is_Pow and self.expr.base == arg.base
-                            and (expr.expr/arg).is_number):
-                            r = (self.expr.exp-arg.exp).is_positive
-                            if not (r is None):
-                                return not r
+            if (self.expr.is_Pow and len(self.variables) == 1
+                and self.variables == expr.variables):
+                    symbol = self.variables[0]
+                    other = expr.expr.as_independent(symbol, as_Add=False)[1]
+                    if (other.is_Pow and other.base == symbol and
+                        self.expr.base == symbol):
+                            if point == S.Zero:
+                                rv = (self.expr.exp - other.exp).is_nonpositive
+                            if point.is_infinite:
+                                rv = (self.expr.exp - other.exp).is_nonnegative
+                            if rv is not None:
+                                return rv
+
             r = None
             ratio = self.expr/expr.expr
             ratio = powsimp(ratio, deep=True, combine='exp')
@@ -395,17 +388,19 @@ def contains(self, expr):
                     if r != l:
                         return
             return r
-        if (self.expr.is_Pow and self.expr.base.is_symbol
-            and self.expr.exp.is_positive):
-            if expr.is_Pow and self.expr.base == expr.base:
-                return not (self.expr.exp-expr.exp).is_positive
-            if expr.is_Mul:
-                for arg in expr.args:
-                    if (arg.is_Pow and self.expr.base == arg.base
-                        and (expr/arg).is_number):
-                        r = (self.expr.exp-arg.exp).is_positive
-                        if not (r is None):
-                            return not r
+
+        if self.expr.is_Pow and len(self.variables) == 1:
+            symbol = self.variables[0]
+            other = expr.as_independent(symbol, as_Add=False)[1]
+            if (other.is_Pow and other.base == symbol and
+                self.expr.base == symbol):
+                    if point == S.Zero:
+                        rv = (self.expr.exp - other.exp).is_nonpositive
+                    if point.is_infinite:
+                        rv = (self.expr.exp - other.exp).is_nonnegative
+                    if rv is not None:
+                        return rv
+
         obj = self.func(expr, *self.args[1:])
         return self.contains(obj)
 


Test patch:
diff --git a/sympy/series/tests/test_order.py b/sympy/series/tests/test_order.py
index 5844a7a181..736b14d13f 100644
--- a/sympy/series/tests/test_order.py
+++ b/sympy/series/tests/test_order.py
@@ -419,3 +419,12 @@ def test_performance_of_adding_order():
     l = list(x**i for i in range(1000))
     l.append(O(x**1001))
     assert Add(*l).subs(x,1) == O(1)
+
+def test_issue_14622():
+    assert (x**(-4) + x**(-3) + x**(-1) + O(x**(-6), (x, oo))).as_numer_denom() == (
+        x**4 + x**5 + x**7 + O(x**2, (x, oo)), x**8)
+    assert (x**3 + O(x**2, (x, oo))).is_Add
+    assert O(x**2, (x, oo)).contains(x**3) is False
+    assert O(x, (x, oo)).contains(O(x, (x, 0))) is None
+    assert O(x, (x, 0)).contains(O(x, (x, oo))) is None
+    raises(NotImplementedError, lambda: O(x**3).contains(x**w))