Skip to content

Commit

Permalink
cancel returns coeff
Browse files Browse the repository at this point in the history
  • Loading branch information
smichr committed Oct 2, 2024
1 parent 4d56c42 commit 7de12b1
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 20 deletions.
27 changes: 26 additions & 1 deletion sympy/polys/euclidtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1859,6 +1859,27 @@ def dup_cancel(f, g, K, include=True):
return dmp_cancel(f, g, 0, K, include=include)


def dmp_ZZ_gcd(f, K):
from sympy.core.intfunc import igcd
assert not f or set([type(i) for i in f]) in [{int},{list}],f
def flat(i):
if type(i) is list:
for i in i:
for j in flat(i):
yield j
else:
yield i
_flat = list(flat(f))
if not _flat:
return K.one, f
if len(_flat) == 1:
c = _flat[0]
else:
c = igcd(*_flat)
if c - 1:
f = [i//c for i in f] if f and type(f[0]) is int else [[i//c for i in j] for j in f]
return c, f

def dmp_cancel(f, g, u, K, include=True):
"""
Cancel common factors in a rational function `f/g`.
Expand All @@ -1881,6 +1902,10 @@ def dmp_cancel(f, g, u, K, include=True):
cq, f = dmp_clear_denoms(f, u, K0, K, convert=True)
cp, g = dmp_clear_denoms(g, u, K0, K, convert=True)
# domain is now K
elif K.is_ZZ:
# remove gcd
cp, f = dmp_ZZ_gcd(f, K)
cq, g = dmp_ZZ_gcd(g, K)
else:
cp, cq = K.one, K.one

Expand All @@ -1904,7 +1929,7 @@ def dmp_cancel(f, g, u, K, include=True):
elif q_neg:
cp, q = -cp, dmp_neg(q, u, K)

if include or (c:=(cp,cq)).count(1) == 1 and c.count(-1) == 0:
if include:
p = dmp_mul_ground(p, cp, u, K)
q = dmp_mul_ground(q, cq, u, K)
cp = cq = 1
Expand Down
29 changes: 26 additions & 3 deletions sympy/polys/polytools.py
Original file line number Diff line number Diff line change
Expand Up @@ -7381,7 +7381,13 @@ def cancel(f, *gens, _signsimp=True, **args):
if not isinstance(f, Tuple):
return f.expand()
else:
return S.One, p, q
if q:
r = p/q
else:
r = p or 1
if r < 0:
return -r, S.NegativeOne, S.One
return r, S.One, S.One
except PolynomialError as msg:
if f.is_commutative and not f.has(Piecewise):
raise PolynomialError(msg)
Expand All @@ -7406,14 +7412,31 @@ def cancel(f, *gens, _signsimp=True, **args):
pass
return f.xreplace(dict(reps))

c, (P, Q) = 1, F.cancel(G)
P, Q = F.cancel(G)
if opt.get('polys', False) and 'gens' not in opt:
opt['gens'] = R.symbols

if not isinstance(f, Tuple):
return c*(P.as_expr()/Q.as_expr())
return P.as_expr()/Q.as_expr()
else:
P, Q = P.as_expr(), Q.as_expr()
cp, _ = factor_terms(P).as_coeff_Mul()
cq, _ = factor_terms(Q).as_coeff_Mul()
if cp and cq:
c = cp/cq
Q = Q/cq
P = P/cp
elif cp:
c = cp
P = P/cp
elif cq:
c = 1/cq
Q = Q/cq
else:
c = S.One
if c < 0:
P = -P
c = -c
if not opt.get('polys', False):
return c, P, Q
else:
Expand Down
6 changes: 3 additions & 3 deletions sympy/polys/tests/test_euclidtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,8 +662,8 @@ def test_dup_cancel():
p = 2*x + 2
q = x - 1

assert R.dup_cancel(f, g) == (p, q)
assert R.dup_cancel(f, g, include=False) == (1, 1, p, q)
assert R.dup_cancel(f, g) == (p, q),R.dup_cancel(f, g)
assert R.dup_cancel(f, g, include=False) == (2, 1, p/2, q)

f = -x - 2
g = 3*x - 4
Expand Down Expand Up @@ -700,7 +700,7 @@ def test_dmp_cancel():
q = x - 1

assert R.dmp_cancel(f, g) == (p, q)
assert R.dmp_cancel(f, g, include=False) == (1, 1, p, q)
assert R.dmp_cancel(f, g, include=False) == (2, 1, p/2, q)

assert R.dmp_cancel(0, 0) == (0, 0)
assert R.dmp_cancel(0, 0, include=False) == (1, 1, 0, 0)
Expand Down
25 changes: 12 additions & 13 deletions sympy/polys/tests/test_polytools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3301,7 +3301,7 @@ def test_ptcancel():
raises(ValueError, lambda: cancel((1, 2, 3)))

# tests first tuple return
assert (t:=cancel((2, 3))) == (1, 2, 3)
assert (t:=cancel((2, 3))) == (S(2)/3, 1, 1)
assert isinstance(t, tuple)

# tests 2nd tuple return
Expand All @@ -3311,11 +3311,11 @@ def test_ptcancel():

# issue 27906
p, q = Poly(x**2/4 - 1), Poly(x/2 - 1)
zz = (1, Poly(x + 2), Poly(2, x))
qq = (1, Poly(x + 2, domain=QQ), Poly(2, x, domain=QQ))
zz = (S.Half, Poly(x + 2), Poly(1, x))
qq = (S.Half, Poly(x + 2, domain=QQ), Poly(1, x, domain=QQ))
assert p.cancel(q, include=False) == qq
case1 = p.cancel(q)
p, q = [Poly(i) for i in (p/q).as_expr().as_numer_denom()]
p, q = [Poly(4*i) for i in (p, q)]
assert p.cancel(q, include=False) == zz
case2 = p.cancel(q)
c,n,d = case1
Expand All @@ -3328,15 +3328,15 @@ def test_ptcancel():
f, g, p, q = 4*x**2 - 4, 2*x - 2, 2*x + 2, 1
F, G, P, Q = [ Poly(u, x) for u in (f, g, p, q) ]

assert F.cancel(G) == (1, P, Q)
assert cancel((f, g)) == (1, p, q)
assert cancel((f, g), x) == (1, p, q)
assert cancel((f, g), (x,)) == (1, p, q)
assert F.cancel(G) == (2, P/2, Q)
assert cancel((f, g)) == (2, p/2, q)
assert cancel((f, g), x) == (2, p/2, q)
assert cancel((f, g), (x,)) == (2, p/2, q)
# tests 3rd tuple return
assert (t:=cancel((F, G))) == (1, P, Q)
assert (t:=cancel((F, G))) == (2, P/2, Q)
assert isinstance(t, tuple)
assert cancel((f, g), polys=True) == (1, P, Q)
assert cancel((F, G), polys=False) == (1, p, q)
assert cancel((f, g), polys=True) == (2, P/2, Q)
assert cancel((F, G), polys=False) == (2, p/2, q)

f = (x**2 - 2)/(x + sqrt(2))

Expand All @@ -3348,8 +3348,7 @@ def test_ptcancel():
assert cancel(f) == f
assert cancel(f, greedy=False) == x + sqrt(2)

assert cancel((x**2/4 - 1, x/2 - 1)) == (1, x + 2, 2)
# assert cancel((x**2/4 - 1, x/2 - 1)) == (S.Half, x + 2, 1)
assert cancel((x**2/4 - 1, x/2 - 1)) == (S.Half, x + 2, 1)

assert cancel((x**2 - y)/(x - y)) == 1/(x - y)*(x**2 - y)

Expand Down

0 comments on commit 7de12b1

Please sign in to comment.