diff --git a/maths/polynomials/__init__.py b/maths/polynomials/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/maths/polynomials/single_indeterminate_operations.py b/maths/polynomials/single_indeterminate_operations.py new file mode 100644 index 00000000..8bafdb59 --- /dev/null +++ b/maths/polynomials/single_indeterminate_operations.py @@ -0,0 +1,188 @@ +""" + +This module implements a single indeterminate polynomials class +with some basic operations + +Reference: https://en.wikipedia.org/wiki/Polynomial + +""" + +from __future__ import annotations + +from collections.abc import MutableSequence + + +class Polynomial: + def __init__(self, degree: int, coefficients: MutableSequence[float]) -> None: + """ + The coefficients should be in order of degree, from smallest to largest. + >>> p = Polynomial(2, [1, 2, 3]) + >>> p = Polynomial(2, [1, 2, 3, 4]) + Traceback (most recent call last): + ... + ValueError: The number of coefficients should be equal to the degree + 1. + + """ + if len(coefficients) != degree + 1: + raise ValueError( + "The number of coefficients should be equal to the degree + 1." + ) + + self.coefficients: list[float] = list(coefficients) + self.degree = degree + + def __add__(self, polynomial_2: Polynomial) -> Polynomial: + """ + Polynomial addition + >>> p = Polynomial(2, [1, 2, 3]) + >>> q = Polynomial(2, [1, 2, 3]) + >>> p + q + 6x^2 + 4x + 2 + """ + + if self.degree > polynomial_2.degree: + coefficients = self.coefficients[:] + for i in range(polynomial_2.degree + 1): + coefficients[i] += polynomial_2.coefficients[i] + return Polynomial(self.degree, coefficients) + else: + coefficients = polynomial_2.coefficients[:] + for i in range(self.degree + 1): + coefficients[i] += self.coefficients[i] + return Polynomial(polynomial_2.degree, coefficients) + + def __sub__(self, polynomial_2: Polynomial) -> Polynomial: + """ + Polynomial subtraction + >>> p = Polynomial(2, [1, 2, 4]) + >>> q = Polynomial(2, [1, 2, 3]) + >>> p - q + 1x^2 + """ + return self + polynomial_2 * Polynomial(0, [-1]) + + def __neg__(self) -> Polynomial: + """ + Polynomial negation + >>> p = Polynomial(2, [1, 2, 3]) + >>> -p + - 3x^2 - 2x - 1 + """ + return Polynomial(self.degree, [-c for c in self.coefficients]) + + def __mul__(self, polynomial_2: Polynomial) -> Polynomial: + """ + Polynomial multiplication + >>> p = Polynomial(2, [1, 2, 3]) + >>> q = Polynomial(2, [1, 2, 3]) + >>> p * q + 9x^4 + 12x^3 + 10x^2 + 4x + 1 + """ + coefficients: list[float] = [0] * (self.degree + polynomial_2.degree + 1) + for i in range(self.degree + 1): + for j in range(polynomial_2.degree + 1): + coefficients[i + j] += ( + self.coefficients[i] * polynomial_2.coefficients[j] + ) + + return Polynomial(self.degree + polynomial_2.degree, coefficients) + + def evaluate(self, substitution: int | float) -> int | float: + """ + Evaluates the polynomial at x. + >>> p = Polynomial(2, [1, 2, 3]) + >>> p.evaluate(2) + 17 + """ + result: int | float = 0 + for i in range(self.degree + 1): + result += self.coefficients[i] * (substitution**i) + return result + + def __str__(self) -> str: + """ + >>> p = Polynomial(2, [1, 2, 3]) + >>> print(p) + 3x^2 + 2x + 1 + """ + polynomial = "" + for i in range(self.degree, -1, -1): + if self.coefficients[i] == 0: + continue + elif self.coefficients[i] > 0: + if polynomial: + polynomial += " + " + else: + polynomial += " - " + + if i == 0: + polynomial += str(abs(self.coefficients[i])) + elif i == 1: + polynomial += str(abs(self.coefficients[i])) + "x" + else: + polynomial += str(abs(self.coefficients[i])) + "x^" + str(i) + + return polynomial + + def __repr__(self) -> str: + """ + >>> p = Polynomial(2, [1, 2, 3]) + >>> p + 3x^2 + 2x + 1 + """ + return self.__str__() + + def derivative(self) -> Polynomial: + """ + Returns the derivative of the polynomial. + >>> p = Polynomial(2, [1, 2, 3]) + >>> p.derivative() + 6x + 2 + """ + coefficients: list[float] = [0] * self.degree + for i in range(self.degree): + coefficients[i] = self.coefficients[i + 1] * (i + 1) + return Polynomial(self.degree - 1, coefficients) + + def integral(self, constant: int | float = 0) -> Polynomial: + """ + Returns the integral of the polynomial. + >>> p = Polynomial(2, [1, 2, 3]) + >>> p.integral() + 1.0x^3 + 1.0x^2 + 1.0x + """ + coefficients: list[float] = [0] * (self.degree + 2) + coefficients[0] = constant + for i in range(self.degree + 1): + coefficients[i + 1] = self.coefficients[i] / (i + 1) + return Polynomial(self.degree + 1, coefficients) + + def __eq__(self, polynomial_2: object) -> bool: + """ + Checks if two polynomials are equal. + >>> p = Polynomial(2, [1, 2, 3]) + >>> q = Polynomial(2, [1, 2, 3]) + >>> p == q + True + """ + if not isinstance(polynomial_2, Polynomial): + return False + + if self.degree != polynomial_2.degree: + return False + + for i in range(self.degree + 1): + if self.coefficients[i] != polynomial_2.coefficients[i]: + return False + + return True + + def __ne__(self, polynomial_2: object) -> bool: + """ + Checks if two polynomials are not equal. + >>> p = Polynomial(2, [1, 2, 3]) + >>> q = Polynomial(2, [1, 2, 3]) + >>> p != q + False + """ + return not self.__eq__(polynomial_2)