From 838247ac116cddfa3561515258f628a29dbf2c92 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Tue, 7 Jul 2020 14:53:08 -0700 Subject: [PATCH 1/8] Optimize Monomial, Constant to run in constant time, --- polynomial/core.py | 124 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 9 deletions(-) diff --git a/polynomial/core.py b/polynomial/core.py index 87cd295..1e113e8 100644 --- a/polynomial/core.py +++ b/polynomial/core.py @@ -720,8 +720,77 @@ def __init__(self, coefficient=1, degree=1): raise ValueError("Monomial's degree should be a natural number.") if degree < 0: raise ValueError("Polynomials cannot have negative-degree terms.") - coeffs = [coefficient] + [0] * degree - Polynomial.__init__(self, coeffs) + self._degree = degree + self._coeff = coefficient + + def _trim(self): + """Trims self._vector to length. Keeps constant terms.""" + + @property + def terms(self): + """Get the terms of self as a list of tuples in coeff, deg form. + + Terms are returned from largest degree to smallest degree, excluding + any terms with a zero coefficient. + """ + if self._coeff == 0: + return [(0, 0)] + if self._degree == -inf: + return [(0, 0)] + return [(self._coeff, self._degree)] + + @terms.setter + def terms(self, terms): + """Set the terms of self as a list of tuples in coeff, deg form.""" + if not terms: + self._coeff = 0 + elif len(terms) == 1: + self._coeff, self._degree = terms[0] + else: + terms = sorted([term for term in terms if term[0] != 0], key=lambda x: x[1]) + if terms[0][1] == terms[-1][1]: + self._coeff = sum(term[0] for term in terms) + self._degree = terms[0][1] + else: + curr_coeff, curr_deg = terms[0] + termx = [] + for coeff, deg in terms[1:]: + if curr_deg == deg: + curr_coeff += coeff + elif curr_coeff != 0: + if termx: + raise TermError("terms has more than one non-zero term.") + termx.append((curr_coeff, curr_deg)) + curr_coeff = coeff + curr_deg = deg + if termx: + if curr_coeff: + raise TermError("terms has more than one non-zero term.") + self._coeff, self._degree = termx[0] + self.coeff = curr_coeff + self.degree = curr_deg + + @property + def _vector(self): + """Get _vector.""" + if self.degree == -inf: + return [0] + return [0] * self._degree + [self._coeff] + + @_vector.setter + def _vector(self, _vector): + """Set _vector.""" + max_deg = len(_vector) - 1 + is_set = False + for index, coeff in enumerate(reversed(_vector)): + if coeff != 0: + if is_set: + raise TermError("_vector has > 1 non-zero term.") + self._coeff = coeff + self._degree = max_deg - index + is_set = True + if not is_set: + self._coeff = 0 @classmethod def zero_instance(cls): @@ -731,12 +800,27 @@ def zero_instance(cls): @property def coefficient(self): """Return the coefficient of the monomial.""" - return self.a + return self._coeff @coefficient.setter - def coefficient(self, new_value): + def coefficient(self, coeff): """Set the coefficient of the monomial.""" - self.a = new_value + self._coeff = coeff + + @property + def degree(self): + """Return the degree of the monomial.""" + if self._coeff == 0: + self._degree = -inf + elif self._degree == -inf: + self._degree = 0 + + return self._degree + + @degree.setter + def degree(self, degree): + """Set the degree of the monomial.""" + self._degree = degree @extract_polynomial def __mul__(self, other): @@ -745,7 +829,8 @@ def __mul__(self, other): The class which is more permissive will be returned. """ if isinstance(other, Monomial) and self and other: - return Monomial(self.a * other.a, self.degree + other.degree) + return Monomial(self.coefficient * other.coefficient, + self.degree + other.degree) return super().__mul__(other) @extract_polynomial @@ -852,7 +937,28 @@ def __irshift__(self, other): def __repr__(self): """Return repr(self).""" - return "Monomial({0!r}, {1!r})".format(self.a, self.degree) + deg = max(0, self.degree) + return "Monomial({0!r}, {1!r})".format(self.coefficient, deg) + + def __getitem__(self, degree): + """Get the coefficient of the term with the given degree.""" + if isinstance(degree, slice): + return self._vector[degree] + if degree == self.degree: + return self._coeff + if degree > self.degree or degree < 0: + raise IndexError("Attempt to get coefficient of term with \ +degree {0} of a {1}-degree monomial".format(degree, self.degree)) + return 0 + + def __setitem__(self, degree, new_value): + """Set the coefficient of the term with the given degree.""" + if isinstance(degree, slice): + self._vector[degree] = new_value + elif degree == self.degree: + self.coefficient = new_value + else: + raise IndexError("Can not set more than 1 term on Monomial.") class Constant(FixedDegreePolynomial, Monomial, valid_degrees=(0, -inf)): @@ -876,12 +982,12 @@ def __eq__(self, other): @property def const(self): """Return the constant term.""" - return self._vector[0] + return self.coefficient @const.setter def const(self, val): """Set the constant term.""" - self._vector[0] = val + self.coefficient = val @extract_polynomial def __mul__(self, other): From 60f0fd7dbc77eaeab94d7355b7de19b0fb25467b Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Tue, 7 Jul 2020 15:02:23 -0700 Subject: [PATCH 2/8] Fix ZeroPolynomial. --- polynomial/frozen.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/polynomial/frozen.py b/polynomial/frozen.py index 55952e5..2040225 100644 --- a/polynomial/frozen.py +++ b/polynomial/frozen.py @@ -79,10 +79,19 @@ class ZeroPolynomial(Freezable, Constant, valid_degrees=-inf): def __init__(self): """Equivalent to Polynomial().""" Constant.__init__(self, 0) - self._vector = tuple(self._vector) self._trim = self._no_op self._freeze() + @property + def _vector(self): + """Return self._vector.""" + return 0, + + @property + def degree(self): + """Return self.degree.""" + return -inf + @classmethod def zero_instance(cls): """Return an instance of the ZeroPolynomial.""" From 1c7e2e0fb678f47d01de0c0f4c1f0b359d5f23f5 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Tue, 7 Jul 2020 15:07:45 -0700 Subject: [PATCH 3/8] Update frozen.py --- polynomial/frozen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polynomial/frozen.py b/polynomial/frozen.py index 2040225..d113fbd 100644 --- a/polynomial/frozen.py +++ b/polynomial/frozen.py @@ -85,7 +85,7 @@ def __init__(self): @property def _vector(self): """Return self._vector.""" - return 0, + return (0, ) @property def degree(self): From 8177562e97387d9dc70ce7eea559916fa6794c04 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Tue, 7 Jul 2020 15:10:04 -0700 Subject: [PATCH 4/8] Fix some issues in core.py --- polynomial/core.py | 61 +++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/polynomial/core.py b/polynomial/core.py index 1e113e8..48eca2e 100644 --- a/polynomial/core.py +++ b/polynomial/core.py @@ -160,21 +160,18 @@ def __init__(self, iterable, from_monomials=False): Polynomial([(1,4), (2,3), (3,2), (4,1), (5,0)], from_monomials=True) Polynomial(((i + 1, 4 - i) for i in range(5)), from_monomials=True) """ - iterable = list(iterable) - if from_monomials: - for i, monomial in enumerate(iterable): + def monomial_to_tuple(monomial): if isinstance(monomial, Monomial): - iterable[i] = (monomial.a, monomial.degree) - elif len(monomial) == 2: - continue - else: - raise TypeError("{} cannot be a monomial.". - format(monomial)) - self.terms = iterable + return monomial.a, monomial.degree + if len(monomial) == 2: + return monomial + + raise TypeError("{} cannot be a monomial.". + format(monomial)) + self.terms = [monomial_to_tuple(monomial) for monomial in iterable] else: - self._vector = iterable[::-1] - self._trim() + self._vector = _trim(list(iterable)[::-1]) @classmethod def zero_instance(cls): @@ -252,14 +249,14 @@ def terms(self, terms): if not terms: _vector = [0] else: - max_deg = max(terms, key=lambda x: x[1])[1] + 1 - _vector = [0] * max_deg + list_len = max(terms, key=lambda x: x[1])[1] + 1 + _vector = [0] * list_len for coeff, deg in terms: _vector[deg] += coeff + _vector = _trim(_vector) self._vector = _vector - self._trim() @property def monomials(self): @@ -457,9 +454,8 @@ def __pos__(self): def __neg__(self): """Return -self.""" - _vector = [-x for x in _trim(self._vector)] ret_val = deepcopy(self) - ret_val._vector = _vector + ret_val._vector = [-x for x in _trim(self._vector)] return ret_val @extract_polynomial @@ -596,6 +592,18 @@ def __rshift__(self, other): def __irshift__(self, other): """Return self >>= other. + Decreases the degree of each term by other. + """ + if other < 0: + self <<= -other + else: + self._vector = _trim(self._vector[other:]) + + return self + + def irshift0(self, other): + """Return self >>= other. + Decreases the degree of each term by other. """ if other < 0: @@ -606,6 +614,18 @@ def __irshift__(self, other): return self + def irshift1(self, other): + """Return self >>= other. + + Decreases the degree of each term by other. + """ + if other < 0: + self <<= -other + else: + self._vector = _trim(self._vector[other:]) + + return self + def __contains__(self, item): """Return item in self. @@ -644,9 +664,10 @@ def try_set_self(self, terms): def setvalue_decorator(error, _terms_are_valid, _fn): """Decorate __setattr__, checking if self._vector is still valid.""" - def method(self, *args, **kwargs): - _fn(self, *args, **kwargs) - if not _terms_are_valid(self, _to_terms(self._vector)): + def method(self, name, new_value): + _fn(self, name, new_value) + if (name == '_vector' and + not _terms_are_valid(self, _to_terms(self._vector))): raise error return method From 7a8b2437d18351bd8af194daeb33d573f78d839f Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Tue, 7 Jul 2020 15:17:21 -0700 Subject: [PATCH 5/8] Fix formatting issues, remove dummy functions. --- polynomial/core.py | 43 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/polynomial/core.py b/polynomial/core.py index 48eca2e..a10fba5 100644 --- a/polynomial/core.py +++ b/polynomial/core.py @@ -601,31 +601,6 @@ def __irshift__(self, other): return self - def irshift0(self, other): - """Return self >>= other. - - Decreases the degree of each term by other. - """ - if other < 0: - self <<= -other - else: - self._vector = self._vector[other:] - self._trim() - - return self - - def irshift1(self, other): - """Return self >>= other. - - Decreases the degree of each term by other. - """ - if other < 0: - self <<= -other - else: - self._vector = _trim(self._vector[other:]) - - return self - def __contains__(self, item): """Return item in self. @@ -768,11 +743,13 @@ def terms(self, terms): elif len(terms) == 1: self._coeff, self._degree = terms[0] else: - terms = sorted([term for term in terms if term[0] != 0], key=lambda x: x[1]) + terms = sorted([term for term in terms if term[0] != 0], + key=lambda x: x[1]) if terms[0][1] == terms[-1][1]: self._coeff = sum(term[0] for term in terms) self._degree = terms[0][1] else: + err_msg = "terms has more than one non-zero term." curr_coeff, curr_deg = terms[0] termx = [] for coeff, deg in terms[1:]: @@ -780,16 +757,16 @@ def terms(self, terms): curr_coeff += coeff elif curr_coeff != 0: if termx: - raise TermError("terms has more than one non-zero term.") + raise TermError(err_msg) termx.append((curr_coeff, curr_deg)) - curr_coeff = coeff - curr_deg = deg + curr_coeff, curr_deg = coeff, deg if termx: if curr_coeff: - raise TermError("terms has more than one non-zero term.") + raise TermError(err_msg) self._coeff, self._degree = termx[0] - self.coeff = curr_coeff - self.degree = curr_deg + else: + self.coeff = curr_coeff + self.degree = curr_deg @property def _vector(self): @@ -851,7 +828,7 @@ def __mul__(self, other): """ if isinstance(other, Monomial) and self and other: return Monomial(self.coefficient * other.coefficient, - self.degree + other.degree) + self.degree + other.degree) return super().__mul__(other) @extract_polynomial From 345aa70458003dd2e68f14262965aea574c74e33 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Tue, 7 Jul 2020 17:14:45 -0700 Subject: [PATCH 6/8] Fix all bugs --- polynomial/core.py | 53 ++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/polynomial/core.py b/polynomial/core.py index a10fba5..53744a4 100644 --- a/polynomial/core.py +++ b/polynomial/core.py @@ -34,6 +34,7 @@ def extract_polynomial(method): If casting is not possible or not appropriate, raise a ValueError. """ + def decorated(self, other): if isinstance(other, Polynomial): return method(self, other) @@ -42,7 +43,7 @@ def decorated(self, other): raise ValueError( "{0}.{1} requires a Polynomial or number, got {2}." - .format( + .format( self.__class__.__name__, method.__name__, type(other).__name__ @@ -90,7 +91,7 @@ def _degree(vec, tuples=True): def _mul(lhs, rhs): """Return lhs * rhs.""" if not lhs or not rhs: - return + return [(0, 0)] deg = _degree(lhs) + _degree(rhs) + 1 res = [0] * deg @@ -169,6 +170,7 @@ def monomial_to_tuple(monomial): raise TypeError("{} cannot be a monomial.". format(monomial)) + self.terms = [monomial_to_tuple(monomial) for monomial in iterable] else: self._vector = _trim(list(iterable)[::-1]) @@ -229,7 +231,7 @@ def nth_derivative(self, n=1): return Polynomial( [c * x for c, x - in zip(self, reversed(factors))] + in zip(self, reversed(factors))] ) @property @@ -366,7 +368,7 @@ def components(ak, k, is_leading): # eg. - 5 x^ 2 s_d = self.degree terms = ["{0}{1}{2}{3}". - format(*components(ak, k, k == s_d)) + format(*components(ak, k, k == s_d)) for ak, k in self.terms] return " ".join(terms) @@ -619,7 +621,7 @@ def __contains__(self, item): raise ValueError( "Can not check {0} for membership. A two-tuple, list of " "two-tuples, a set, or a Polynomial are required." - .format(type(item).__name__) + .format(type(item).__name__) ) def terms_are_valid(self, terms): @@ -639,11 +641,13 @@ def try_set_self(self, terms): def setvalue_decorator(error, _terms_are_valid, _fn): """Decorate __setattr__, checking if self._vector is still valid.""" + def method(self, name, new_value): _fn(self, name, new_value) if (name == '_vector' and - not _terms_are_valid(self, _to_terms(self._vector))): + not _terms_are_valid(self, _to_terms(self._vector))): raise error + return method @@ -654,7 +658,7 @@ def __init_subclass__(cls, **kwargs): """Init a subclass of self.""" deg = kwargs["valid_degrees"] if not isinstance(deg, tuple): - deg = (deg, ) + deg = (deg,) cls.valid_degrees = deg @@ -731,8 +735,6 @@ def terms(self): """ if self._coeff == 0: return [(0, 0)] - if self._degree == -inf: - return [(0, 0)] return [(self._coeff, self._degree)] @terms.setter @@ -752,21 +754,24 @@ def terms(self, terms): err_msg = "terms has more than one non-zero term." curr_coeff, curr_deg = terms[0] termx = [] + for coeff, deg in terms[1:]: if curr_deg == deg: curr_coeff += coeff - elif curr_coeff != 0: - if termx: - raise TermError(err_msg) - termx.append((curr_coeff, curr_deg)) - curr_coeff, curr_deg = coeff, deg + else: + if curr_coeff != 0: + if termx: + raise TermError(err_msg) + termx.append((curr_coeff, curr_deg)) + curr_coeff = coeff + curr_deg = deg if termx: if curr_coeff: raise TermError(err_msg) self._coeff, self._degree = termx[0] else: - self.coeff = curr_coeff - self.degree = curr_deg + self._coeff = curr_coeff + self._degree = curr_deg @property def _vector(self): @@ -952,11 +957,13 @@ def __getitem__(self, degree): def __setitem__(self, degree, new_value): """Set the coefficient of the term with the given degree.""" if isinstance(degree, slice): - self._vector[degree] = new_value + _vector = self._vector + _vector[degree] = new_value + self._vector = _vector elif degree == self.degree: self.coefficient = new_value else: - raise IndexError("Can not set more than 1 term on Monomial.") + raise TermError("Can not set more than 1 term on Monomial.") class Constant(FixedDegreePolynomial, Monomial, valid_degrees=(0, -inf)): @@ -977,6 +984,16 @@ def __eq__(self, other): return True return super().__eq__(other) + @property + def degree(self): + """Return self.degree.""" + return 0 if self._coeff else -inf + + @degree.setter + def degree(self, degree): + """Set self.degree""" + raise DegreeError("Can't change the degree of Constant") + @property def const(self): """Return the constant term.""" From 8476cc3c6d3e5f21689e610241414486fc0b87c0 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Tue, 7 Jul 2020 17:15:46 -0700 Subject: [PATCH 7/8] Full test coverage. --- tests/test_polynomials_operations.py | 92 +++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/tests/test_polynomials_operations.py b/tests/test_polynomials_operations.py index 4c238f0..bcb04e4 100644 --- a/tests/test_polynomials_operations.py +++ b/tests/test_polynomials_operations.py @@ -310,7 +310,7 @@ def test_divmod_against_constant(self): p3, remainder = divmod(p1, p2) - self.assertEqual(p3, Polynomial(1/5, 2/5, 3/5)) + self.assertEqual(p3, Polynomial(1 / 5, 2 / 5, 3 / 5)) self.assertEqual(remainder, Polynomial()) def test_divmod_against_monomial(self): @@ -410,7 +410,7 @@ def test_membership_against_invalid_types(self): self.assertRaises(ValueError, x.__contains__, "5") self.assertRaises(ValueError, x.__contains__, "a") self.assertRaises(ValueError, x.__contains__, 1.2) - self.assertRaises(ValueError, x.__contains__, 1+0j) + self.assertRaises(ValueError, x.__contains__, 1 + 0j) def test_membership_false_on_partial_match(self): """Tests that membership is only true if all elements match.""" @@ -913,6 +913,7 @@ def test_frozen_zero_instance_immutable(self): def test_freezable_obeys_frozen_flag(self): """Test that Freezable objects behave as expected.""" + class A: def __init__(self): self._list = [1, 2, 3] @@ -1001,16 +1002,16 @@ def test_linear_binomial_roots(self): self.assertEqual(a.root, 0) a = LinearBinomial(11, 5) - self.assertEqual(a.root, -5/11) + self.assertEqual(a.root, -5 / 11) a = LinearBinomial(12.1, 3) - self.assertEqual(a.root, -3/12.1) + self.assertEqual(a.root, -3 / 12.1) a = LinearBinomial(13.2, 3.6) - self.assertEqual(a.root, -3.6/13.2) + self.assertEqual(a.root, -3.6 / 13.2) a = LinearBinomial(5 + 10j, 2 + 1j) - self.assertEqual(a.root, -(2 + 1j)/(5 + 10j)) + self.assertEqual(a.root, -(2 + 1j) / (5 + 10j)) def test_pos(self): """Test that a Polynomial is equal to its positive version.""" @@ -1159,7 +1160,7 @@ def test_ipow_zero_gives_one(self): val **= 0 self._assert_polynomials_are_the_same(expected, val) - def test_getitem(self): + def test_polynomial_getitem(self): """Test getitem on slices, out of bounds, and in bound indices.""" a = Polynomial(6, 5, 4, 3, 2, 1) self.assertEqual([1, 2, 3, 4, 5, 6], a[:]) @@ -1169,8 +1170,22 @@ def test_getitem(self): self.assertRaises(IndexError, a.__getitem__, -inf) self.assertRaises(IndexError, a.__getitem__, 6) - def test_setitem(self): + a = Polynomial(0) + self.assertEqual(a[-inf], 0) + + def test_monomial_getitem(self): """Test getitem on slices, out of bounds, and in bound indices.""" + m = Monomial(1, 10) + self.assertEqual([0] * 10 + [1], m[:]) + self.assertEqual([0, 0], m[:2]) + self.assertEqual([0, 0, 0, 1], m[7:]) + self.assertEqual(1, m[10]) + self.assertEqual(0, m[3]) + self.assertRaises(IndexError, m.__getitem__, -inf) + self.assertRaises(IndexError, m.__getitem__, 11) + + def test_polynomial_setitem(self): + """Test setitem on slices, out of bounds, and in bound indices.""" a = Polynomial(6, 5, 4, 3, 2, 1) a[:] = [1, 2, 3, 4, 5, 6] self.assertEqual([1, 2, 3, 4, 5, 6], a[:]) @@ -1181,6 +1196,59 @@ def test_setitem(self): self.assertRaises(IndexError, a.__setitem__, -inf, 1) self.assertRaises(IndexError, a.__setitem__, 6, 1) + def test_monomial_setitem(self): + """Test setitem on slices, out of bounds, and in bound indices.""" + m = Monomial(6, 5) + self.assertRaises(TermError, m.__setitem__, slice(0, -1, 1), [1, 2, 3, 4, 5, 6]) + self.assertRaises(TermError, m.__setitem__, 1, 3) + m[6] = 10 + self.assertEqual(10, m[6]) + + def test_monomial_vector(self): + """Test _vector for Monomials.""" + m = Monomial(6, 5) + self.assertEqual([0, 0, 0, 0, 0, 6], m._vector) + m._vector = [0, 0, 0, 0] + self.assertEqual(0, m.coefficient) + + def test_monomial_terms(self): + """Test _vector for Monomials.""" + m = Monomial(6, 5) + m.coefficient = 0 + self.assertEqual([(0, 0)], m.terms) + m.terms = [(10, 5), (10, 5), (10, 5), (10, 5)] + self.assertEqual(40, m.coefficient) + self.assertEqual(5, m.degree) + + m.terms = [(10, 5), (10, 5), (0, 4), (0, 3), + (0, 2), (0, 1), (10, 5), (10, 5)] + self.assertEqual(40, m.coefficient) + self.assertEqual(5, m.degree) + + m.terms = [(25, 6), (-25, 6), (25, 4)] + self.assertEqual(25, m.coefficient) + self.assertEqual(4, m.degree) + + m.terms = [(25, 6), (-25, 5), (25, 5)] + self.assertEqual(25, m.coefficient) + self.assertEqual(6, m.degree) + + bad_terms = [(25, 6), (-25, 6), (25, 4), (25, 3)] + self.assertRaises(TermError, m.__setattr__, "terms", bad_terms) + + def test_monomial_set_degree(self): + """Test setting Monomial degree""" + m = Monomial(6, 5) + self.assertEqual(5, m.degree) + m.degree = 4 + self.assertEqual(4, m.degree) + + def test_constant_set_degree(self): + """Test setting Monomial degree""" + c = Constant(6) + self.assertEqual(0, c.degree) + self.assertRaises(DegreeError, c.__setattr__, "degree", 1) + def test_extract_polynomial_raises_errors(self): """Test that extract_polynomial errors on bad input.""" bad_inputs = ["a", [1, 2, 3], (1, 2), None] @@ -1233,7 +1301,7 @@ def eqn(x): return x ** 2 + 2 * x + 3 for i in range(-100, 100): - i = i/100 + i = i / 100 self.assertEqual(eqn(i), a.calculate(i)) def test_calculate_zero_polynomial(self): @@ -1289,10 +1357,16 @@ def test_degree(self): from polynomial.core import _degree self.assertEqual(_degree([(0, 2), (0, 1), (1, 0)]), 0) + def test_mul(self): + """Test mul handles empty terms correctly.""" + from polynomial.core import _mul + self.assertEqual(_mul([], [(1, 2), (3, 1)]), [(0, 0)]) + def test_add(self): """Test add handles empty terms correctly.""" from polynomial.core import _add self.assertEqual(_add([], [(1, 2), (3, 1)]), [(1, 2), (3, 1)]) + self.assertEqual(_add([(1, 2), (3, 1)], []), [(1, 2), (3, 1)]) def test_sub(self): """Test sub handles empty terms correctly.""" From 10a121a22d78186dd5cc6e431b351f5c19b8c999 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Tue, 7 Jul 2020 17:17:24 -0700 Subject: [PATCH 8/8] Update core.py --- polynomial/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polynomial/core.py b/polynomial/core.py index 53744a4..15dc07f 100644 --- a/polynomial/core.py +++ b/polynomial/core.py @@ -991,7 +991,7 @@ def degree(self): @degree.setter def degree(self, degree): - """Set self.degree""" + """Set self.degree.""" raise DegreeError("Can't change the degree of Constant") @property