Repository: diofant/diofant

Issue:
Inconsistency quo vs quo_ground in dense and sparse representations
Here is an example:
```python
In [1]: e = 2*x + 3                                                                                                                     

In [2]: dense = e.as_poly()                                                                                                             

In [3]: dense.quo(2, auto=False)                                                                                                        
Out[3]: Poly(x, x, domain='ZZ')

In [4]: dense.quo_ground(2)                                                                                                             
Out[4]: Poly(x + 1, x, domain='ZZ')

In [5]: R = ZZ.poly_ring(x)                                                                                                             

In [6]: sparse = R.from_expr(e)                                                                                                         

In [7]: sparse.quo(R.from_expr(2))                                                                                                      
Out[7]: x

In [8]: sparse.quo_ground(2)                                                                                                            
Out[8]: x
```

For sparse representation ``quo()`` and ``quo_ground()`` outputs are equal, but for dense (which currently uses Poly class) - are not.

@jksuom, what do you think (this issue is valid for sympy as well, if you replace from_expr -> from_sympy)?


Discussion:

Patch:
diff --git a/diofant/polys/ring_series.py b/diofant/polys/ring_series.py
index e1190df0d..4637deb85 100644
--- a/diofant/polys/ring_series.py
+++ b/diofant/polys/ring_series.py
@@ -534,7 +534,7 @@ def rs_compose_add(p1, p2):
     np2e = rs_hadamard_exp(np2)
     np3e = rs_mul(np1e, np2e, x, prec)
     np3 = rs_hadamard_exp(np3e, True)
-    np3a = (np3[(0,)] - np3)/x
+    np3a = (np3[(0,)] - np3)//x
     q = rs_integrate(np3a, x)
     q = rs_exp(q, x, prec)
     q = _invert_monoms(q)
diff --git a/diofant/polys/rings.py b/diofant/polys/rings.py
index 8c8e9ae66..5bb1bbbfc 100644
--- a/diofant/polys/rings.py
+++ b/diofant/polys/rings.py
@@ -1190,7 +1190,7 @@ def __mod__(self, other):
     def __rmod__(self, other):
         return NotImplemented
 
-    def __truediv__(self, other):
+    def __floordiv__(self, other):
         ring = self.ring
 
         if not other:
@@ -1201,7 +1201,7 @@ def __truediv__(self, other):
             if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == other.ring:
                 pass
             elif isinstance(other.ring.domain, PolynomialRing) and other.ring.domain.ring == ring:
-                return other.__rtruediv__(self)
+                return other.__rfloordiv__(self)
             else:
                 return NotImplemented
 
@@ -1212,13 +1212,26 @@ def __truediv__(self, other):
         else:
             return self.quo_ground(other)
 
-    def __rtruediv__(self, other):
+    def __rfloordiv__(self, other):
         return NotImplemented
 
-    __floordiv__ = __truediv__
-    __rfloordiv__ = __rtruediv__
+    def __truediv__(self, other):
+        ring = self.ring
+
+        if not other:
+            raise ZeroDivisionError("polynomial division")
+        elif isinstance(other, ring.domain.dtype):
+            return self.quo_ground(other)
 
-    # TODO: use // (__floordiv__) for exquo()?
+        try:
+            other = ring.domain_new(other)
+        except CoercionFailed:
+            return NotImplemented
+        else:
+            return self.quo_ground(other)
+
+    def __rtruediv__(self, other):
+        return NotImplemented
 
     def _term_div(self):
         zm = self.ring.zero_monom
@@ -1722,7 +1735,7 @@ def monic(self):
         if not self:
             return self
         else:
-            return self.quo_ground(self.LC)
+            return self.exquo_ground(self.LC)
 
     def mul_ground(self, x):
         if not x:
@@ -1758,9 +1771,25 @@ def quo_ground(self, x):
             quo = domain.quo
             terms = [(monom, quo(coeff, x)) for monom, coeff in self.items()]
         else:
-            terms = [(monom, coeff//x) for monom, coeff in self.items() if not (coeff % x)]
+            terms = [(monom, coeff//x) for monom, coeff in self.items()]
 
-        return self.new(terms)
+        p = self.new(terms)
+        p.strip_zero()
+        return p
+
+    def exquo_ground(self, x):
+        domain = self.ring.domain
+
+        if not x:
+            raise ZeroDivisionError('polynomial division')
+        if not self or x == domain.one:
+            return self
+
+        terms = [(monom, domain.exquo(coeff, x)) for monom, coeff in self.items()]
+
+        p = self.new(terms)
+        p.strip_zero()
+        return p
 
     def quo_term(self, term):
         monom, coeff = term


Test patch:
diff --git a/diofant/domains/tests/test_polynomialring.py b/diofant/domains/tests/test_polynomialring.py
index 88cddd93f..f99f14eb1 100644
--- a/diofant/domains/tests/test_polynomialring.py
+++ b/diofant/domains/tests/test_polynomialring.py
@@ -37,7 +37,7 @@ def test_globalring():
     assert X + Y == R.convert(x + y)
     assert X - Y == R.convert(x - y)
     assert X + 1 == R.convert(x + 1)
-    assert X**2 / X == X
+    assert X**2 // X == X
 
     assert R.convert(ZZ.poly_ring(x, y).convert(x), ZZ.poly_ring(x, y)) == X
     assert R.convert(Qxy.convert(x), Qxy) == X
@@ -56,7 +56,7 @@ def test_localring():
     assert X + Y == R.convert(x + y)
     assert X - Y == R.convert(x - y)
     assert X + 1 == R.convert(x + 1)
-    assert X**2 / X == X
+    assert X**2 // X == X
 
     assert R.convert(ZZ.poly_ring(x, y).convert(x), ZZ.poly_ring(x, y)) == X
     assert R.convert(Qxy.convert(x), Qxy) == X
diff --git a/diofant/polys/tests/test_euclidtools.py b/diofant/polys/tests/test_euclidtools.py
index 87253e1c5..a4a01bff2 100644
--- a/diofant/polys/tests/test_euclidtools.py
+++ b/diofant/polys/tests/test_euclidtools.py
@@ -732,7 +732,7 @@ def test_dmp_primitive():
         g *= f
         F += x**i*g
 
-    assert R.dmp_primitive(F) == (f.drop(x), F / f)
+    assert R.dmp_primitive(F) == (f.drop(x), F // f)
 
     R,  x, y, z = ring("x,y,z", ZZ)
 
diff --git a/diofant/polys/tests/test_rings.py b/diofant/polys/tests/test_rings.py
index 33d06b69a..11e501a90 100644
--- a/diofant/polys/tests/test_rings.py
+++ b/diofant/polys/tests/test_rings.py
@@ -224,8 +224,8 @@ def test_PolyElement___eq__():
     Rt, t = ring("t", ZZ)
     R,  x, y = ring("x,y", Rt)
 
-    assert (t**3*x/x == t**3) is True
-    assert (t**3*x/x == t**4) is False
+    assert (t**3*x//x == t**3) is True
+    assert (t**3*x//x == t**4) is False
 
 
 def test_PolyElement__lt_le_gt_ge__():
@@ -629,17 +629,17 @@ def test_PolyElement___mul__():
     assert p == p2 and p2 == R.zero
 
 
-def test_PolyElement___truediv__():
+def test_PolyElement___floordiv__truediv__():
     R,  x, y, z = ring("x,y,z", ZZ)
 
     assert (2*x**2 - 4)/2 == x**2 - 2
-    assert (2*x**2 - 3)/2 == x**2
+    assert (2*x**2 - 3)/2 == x**2 - 2
 
-    assert (x**2 - 1)/x == (x**2 - 1).quo(x) == x
-    assert (x**2 - x)/x == (x**2 - x).quo(x) == x - 1
+    assert (x**2 - 1)//x == (x**2 - 1).quo(x) == x
+    assert (x**2 - x)//x == (x**2 - x).quo(x) == x - 1
 
-    assert (x**2 - 1)/(2*x) == (x**2 - 1).quo(2*x) == 0
-    assert (x**2 - x)/(x - 1) == (x**2 - x).quo(x - 1) == x
+    assert (x**2 - 1)//(2*x) == (x**2 - 1).quo(2*x) == 0
+    assert (x**2 - x)//(x - 1) == (x**2 - x).quo(x - 1) == x
 
     R,  x, y, z = ring("x,y,z", ZZ)
     assert len((x**2/3 + y**3/4 + z**4/5).terms()) == 0
@@ -647,17 +647,19 @@ def test_PolyElement___truediv__():
     R,  x, y, z = ring("x,y,z", QQ)
     assert len((x**2/3 + y**3/4 + z**4/5).terms()) == 3
 
+    pytest.raises(ZeroDivisionError, lambda: x/0)
+
     Rt, t = ring("t", ZZ)
     Ruv,  u, v = ring("u,v", ZZ)
     Rxyz,  x, y, z = ring("x,y,z", Ruv)
 
     assert dict((u**2*x + u)/u) == {(1, 0, 0): u, (0, 0, 0): 1}
-    pytest.raises(TypeError, lambda: u/(u**2*x + u))
+    pytest.raises(TypeError, lambda: u//(u**2*x + u))
 
-    pytest.raises(TypeError, lambda: t/x)
-    pytest.raises(TypeError, lambda: x/t)
-    pytest.raises(TypeError, lambda: t/u)
-    pytest.raises(TypeError, lambda: u/t)
+    pytest.raises(TypeError, lambda: t//x)
+    pytest.raises(TypeError, lambda: x//t)
+    pytest.raises(TypeError, lambda: t//u)
+    pytest.raises(TypeError, lambda: u//t)
 
     assert divmod(x, u) == (0, x)
     assert x % u == x
@@ -682,7 +684,7 @@ def test_PolyElement___truediv__():
     pytest.raises(ZeroDivisionError, lambda: f.rem(g))
     pytest.raises(ZeroDivisionError, lambda: f % g)
     pytest.raises(ZeroDivisionError, lambda: f.quo(g))
-    pytest.raises(ZeroDivisionError, lambda: f / g)
+    pytest.raises(ZeroDivisionError, lambda: f // g)
     pytest.raises(ZeroDivisionError, lambda: f.exquo(g))
 
     R,  x, y = ring("x,y", ZZ)
@@ -693,7 +695,7 @@ def test_PolyElement___truediv__():
     pytest.raises(ZeroDivisionError, lambda: f.rem(g))
     pytest.raises(ZeroDivisionError, lambda: f % g)
     pytest.raises(ZeroDivisionError, lambda: f.quo(g))
-    pytest.raises(ZeroDivisionError, lambda: f / g)
+    pytest.raises(ZeroDivisionError, lambda: f // g)
     pytest.raises(ZeroDivisionError, lambda: f.exquo(g))
 
     R, x = ring("x", ZZ)
@@ -703,7 +705,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     assert R.zero.div(f) == (R.zero, R.zero)
@@ -714,7 +716,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     f, g = 5*x**4 + 4*x**3 + 3*x**2 + 2*x + 1, x**2 + 2*x + 3
@@ -722,7 +724,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     f, g = 5*x**5 + 4*x**4 + 3*x**3 + 2*x**2 + x, x**4 + 2*x**3 + 9
@@ -730,7 +732,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     R, x = ring("x", QQ)
@@ -740,7 +742,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     f, g = 3*x**3 + x**2 + x + 5, 5*x**2 - 3*x + 1
@@ -748,7 +750,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     R,  x, y = ring("x,y", ZZ)
@@ -758,7 +760,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     assert f.exquo(g) == q
 
     f, g = x**2 + y**2, x - y
@@ -766,7 +768,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     f, g = x**2 + y**2, -x + y
@@ -774,7 +776,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     f, g = x**2 + y**2, 2*x - 2*y
@@ -782,7 +784,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     R,  x, y = ring("x,y", QQ)
@@ -792,7 +794,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     assert f.exquo(g) == q
 
     f, g = x**2 + y**2, x - y
@@ -800,7 +802,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     f, g = x**2 + y**2, -x + y
@@ -808,7 +810,7 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     f, g = x**2 + y**2, 2*x - 2*y
@@ -816,11 +818,14 @@ def test_PolyElement___truediv__():
 
     assert f.div(g) == divmod(f, g) == (q, r)
     assert f.rem(g) == f % g == r
-    assert f.quo(g) == f / g == q
+    assert f.quo(g) == f // g == q
     pytest.raises(ExactQuotientFailed, lambda: f.exquo(g))
 
     pytest.raises(ZeroDivisionError, lambda: f.quo_ground(0))
     pytest.raises(ZeroDivisionError, lambda: f.quo_term(((1, 1), 0)))
+    pytest.raises(ZeroDivisionError, lambda: f.exquo_ground(0))
+
+    assert R.zero.exquo_ground(2) == R.zero
 
     assert R.zero.quo_term(((1, 0), 1)) == R.zero
     assert g.quo_term((R.zero_monom, 2)) == x - y
@@ -959,6 +964,18 @@ def test_PolyElement_rem():
     assert f.rem(G) == f.div(G)[1] == r
 
 
+def test_PolyElement_monic():
+    R, x = ring("x", ZZ)
+
+    assert (2*x + 2).monic() == x + 1
+
+    pytest.raises(ExactQuotientFailed, lambda: (2*x + 1).monic())
+
+    R, x = ring("x", QQ)
+
+    assert (2*x + 1).monic() == x + QQ(1, 2)
+
+
 def test_PolyElement_deflate():
     R, x = ring("x", ZZ)
 
@@ -1112,8 +1129,8 @@ def test_PolyElement_cofactors():
     g = 2.1*x**3
     h = 1.0*x
 
-    assert f.cofactors(g) == (h, f/h, g/h)
-    assert g.cofactors(f) == (h, g/h, f/h)
+    assert f.cofactors(g) == (h, f//h, g//h)
+    assert g.cofactors(f) == (h, g//h, f//h)
 
 
 def test_PolyElement_gcd():