// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include #include #include #include namespace WwiseGTE { template class Polynomial1 { public: // Construction and destruction. The first constructor creates a // polynomial of the specified degree but sets all coefficients to // zero (to ensure initialization). You are responsible for setting // the coefficients, presumably with the degree-term set to a nonzero // number. In the second constructor, the degree is the number of // initializers plus 1, but then adjusted so that coefficient[degree] // is not zero (unless all initializer values are zero). Polynomial1(unsigned int degree = 0) : mCoefficient(degree + 1, (Real)0) { } Polynomial1(std::initializer_list values) { // C++ 11 will call the default constructor for // Polynomial1 p{}, so it is guaranteed that // values.size() > 0. mCoefficient.resize(values.size()); std::copy(values.begin(), values.end(), mCoefficient.begin()); EliminateLeadingZeros(); } // Support for partial construction, where the default constructor is // used when the degree is not yet known. The coefficients are // uninitialized. void SetDegree(unsigned int degree) { mCoefficient.resize(degree + 1); } // Set all coefficients to the specified value. void SetCoefficients(Real value) { std::fill(mCoefficient.begin(), mCoefficient.end(), value); } // Member access. inline unsigned int GetDegree() const { // By design, mCoefficient.size() > 0. return static_cast(mCoefficient.size() - 1); } inline Real const& operator[](unsigned int i) const { return mCoefficient[i]; } inline Real& operator[](unsigned int i) { return mCoefficient[i]; } // Comparisons. inline bool operator==(Polynomial1 const& p) const { return mCoefficient == p.mCoefficient; } inline bool operator!=(Polynomial1 const& p) const { return mCoefficient != p.mCoefficient; } inline bool operator< (Polynomial1 const& p) const { return mCoefficient < p.mCoefficient; } inline bool operator<=(Polynomial1 const& p) const { return mCoefficient <= p.mCoefficient; } inline bool operator> (Polynomial1 const& p) const { return mCoefficient > p.mCoefficient; } inline bool operator>=(Polynomial1 const& p) const { return mCoefficient >= p.mCoefficient; } // Evaluate the polynomial. If the polynomial is invalid, the // function returns zero. Real operator()(Real t) const { int i = static_cast(mCoefficient.size()); Real result = mCoefficient[--i]; for (--i; i >= 0; --i) { result *= t; result += mCoefficient[i]; } return result; } // Compute the derivative of the polynomial. Polynomial1 GetDerivative() const { unsigned int const degree = GetDegree(); if (degree > 0) { Polynomial1 result(degree - 1); for (unsigned int i0 = 0, i1 = 1; i0 < degree; ++i0, ++i1) { result.mCoefficient[i0] = mCoefficient[i1] * (Real)i1; } return result; } else { Polynomial1 result(0); result[0] = (Real)0; return result; } } // Inversion (invpoly[i] = poly[degree-i] for 0 <= i <= degree). Polynomial1 GetInversion() const { unsigned int const degree = GetDegree(); Polynomial1 result(degree); for (unsigned int i = 0; i <= degree; ++i) { result.mCoefficient[i] = mCoefficient[degree - i]; } return result; } // Tranlation. If 'this' is p(t}, return p(t-t0). Polynomial1 GetTranslation(Real t0) const { Polynomial1 factor{ -t0, (Real)1 }; // f(t) = t - t0 unsigned int const degree = GetDegree(); Polynomial1 result{ mCoefficient[degree] }; for (unsigned int i = 1, j = degree - 1; i <= degree; ++i, --j) { result = mCoefficient[j] + factor * result; } return result; } // Eliminate any leading zeros in the polynomial, except in the case // the degree is 0 and the coefficient is 0. The elimination is // necessary when arithmetic operations cause a decrease in the degree // of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) = // (2 + 3*x). The inputs both have degree 2, so the result is created // with degree 2. After the addition we find that the degree is in // fact 1 and resize the array of coefficients. This function is // called internally by the arithmetic operators, but it is exposed in // the public interface in case you need it for your own purposes. void EliminateLeadingZeros() { size_t size = mCoefficient.size(); if (size > 1) { Real const zero = (Real)0; int leading; for (leading = static_cast(size) - 1; leading > 0; --leading) { if (mCoefficient[leading] != zero) { break; } } mCoefficient.resize(++leading); } } // If 'this' is P(t) and the divisor is D(t) with // degree(P) >= degree(D), then P(t) = Q(t)*D(t)+R(t) where Q(t) is // the quotient with degree(Q) = degree(P) - degree(D) and R(t) is the // remainder with degree(R) < degree(D). If this routine is called // with degree(P) < degree(D), then Q = 0 and R = P are returned. void Divide(Polynomial1 const& divisor, Polynomial1& quotient, Polynomial1& remainder) const { Real const zero = (Real)0; int divisorDegree = static_cast(divisor.GetDegree()); int quotientDegree = static_cast(GetDegree()) - divisorDegree; if (quotientDegree >= 0) { quotient.SetDegree(quotientDegree); // Temporary storage for the remainder. Polynomial1 tmp = *this; // Do the division using the Euclidean algorithm. Real inv = ((Real)1) / divisor[divisorDegree]; for (int i = quotientDegree; i >= 0; --i) { int j = divisorDegree + i; quotient[i] = inv * tmp[j]; for (j--; j >= i; j--) { tmp[j] -= quotient[i] * divisor[j - i]; } } // Calculate the correct degree for the remainder. if (divisorDegree >= 1) { int remainderDegree = divisorDegree - 1; while (remainderDegree > 0 && tmp[remainderDegree] == zero) { --remainderDegree; } remainder.SetDegree(remainderDegree); for (int i = 0; i <= remainderDegree; ++i) { remainder[i] = tmp[i]; } } else { remainder.SetDegree(0); remainder[0] = zero; } } else { quotient.SetDegree(0); quotient[0] = zero; remainder = *this; } } // Scale the polynomial so the highest-degree term has coefficient 1. void MakeMonic() { EliminateLeadingZeros(); Real const one(1); if (mCoefficient.back() != one) { unsigned int degree = GetDegree(); Real invLeading = one / mCoefficient.back(); mCoefficient.back() = one; for (unsigned int i = 0; i < degree; ++i) { mCoefficient[i] *= invLeading; } } } protected: // The class is designed so that mCoefficient.size() >= 1. std::vector mCoefficient; }; // Compute the greatest common divisor of two polynomials. The returned // polynomial has leading coefficient 1 (except when zero-valued // polynomials are passed to the function. template Polynomial1 GreatestCommonDivisor(Polynomial1 const& p0, Polynomial1 const& p1) { // The numerator should be the polynomial of larger degree. Polynomial1 a, b; if (p0.GetDegree() >= p1.GetDegree()) { a = p0; b = p1; } else { a = p1; b = p0; } Polynomial1 const zero{ (Real)0 }; if (a == zero || b == zero) { return (a != zero ? a : zero); } // Make the polynomials monic to keep the coefficients reasonable size // when computing with floating-point Real. a.MakeMonic(); b.MakeMonic(); Polynomial1 q, r; for (;;) { a.Divide(b, q, r); if (r != zero) { // a = q * b + r, so gcd(a,b) = gcd(b, r) a = b; b = r; b.MakeMonic(); } else { b.MakeMonic(); break; } } return b; } // Factor f = factor[0]*factor[1]^2*factor[2]^3*...*factor[n-1]^n // according to the square-free factorization algorithm // https://en.wikipedia.org/wiki/Square-free_polynomial template void SquareFreeFactorization(Polynomial1 const& f, std::vector>& factors) { // In the call to Divide(...), we know that the divisor exactly // divides the numerator, so r = 0 after all such calls. Polynomial1 fder = f.GetDerivative(); Polynomial1 a, b, c, d, q, r; a = GreatestCommonDivisor(f, fder); f.Divide(a, b, r); // b = f / a fder.Divide(a, c, r); // c = fder / a d = c - b.GetDerivative(); do { a = GreatestCommonDivisor(b, d); factors.emplace_back(a); b.Divide(a, q, r); // q = b / a b = std::move(q); d.Divide(a, c, r); // c = d / a d = c - b.GetDerivative(); } while (b.GetDegree() > 0); } // Unary operations. template Polynomial1 operator+(Polynomial1 const& p) { return p; } template Polynomial1 operator-(Polynomial1 const& p) { unsigned int const degree = p.GetDegree(); Polynomial1 result(degree); for (unsigned int i = 0; i <= degree; ++i) { result[i] = -p[i]; } return result; } // Linear-algebraic operations. template Polynomial1 operator+(Polynomial1 const& p0, Polynomial1 const& p1) { unsigned int const p0Degree = p0.GetDegree(), p1Degree = p1.GetDegree(); unsigned int i; if (p0Degree >= p1Degree) { Polynomial1 result(p0Degree); for (i = 0; i <= p1Degree; ++i) { result[i] = p0[i] + p1[i]; } for (/**/; i <= p0Degree; ++i) { result[i] = p0[i]; } result.EliminateLeadingZeros(); return result; } else { Polynomial1 result(p1Degree); for (i = 0; i <= p0Degree; ++i) { result[i] = p0[i] + p1[i]; } for (/**/; i <= p1Degree; ++i) { result[i] = p1[i]; } result.EliminateLeadingZeros(); return result; } } template Polynomial1 operator-(Polynomial1 const& p0, Polynomial1 const& p1) { unsigned int const p0Degree = p0.GetDegree(), p1Degree = p1.GetDegree(); unsigned int i; if (p0Degree >= p1Degree) { Polynomial1 result(p0Degree); for (i = 0; i <= p1Degree; ++i) { result[i] = p0[i] - p1[i]; } for (/**/; i <= p0Degree; ++i) { result[i] = p0[i]; } result.EliminateLeadingZeros(); return result; } else { Polynomial1 result(p1Degree); for (i = 0; i <= p0Degree; ++i) { result[i] = p0[i] - p1[i]; } for (/**/; i <= p1Degree; ++i) { result[i] = -p1[i]; } result.EliminateLeadingZeros(); return result; } } template Polynomial1 operator*(Polynomial1 const& p0, Polynomial1 const& p1) { unsigned int const p0Degree = p0.GetDegree(), p1Degree = p1.GetDegree(); Polynomial1 result(p0Degree + p1Degree); result.SetCoefficients((Real)0); for (unsigned int i0 = 0; i0 <= p0Degree; ++i0) { for (unsigned int i1 = 0; i1 <= p1Degree; ++i1) { result[i0 + i1] += p0[i0] * p1[i1]; } } return result; } template Polynomial1 operator+(Polynomial1 const& p, Real scalar) { unsigned int const degree = p.GetDegree(); Polynomial1 result(degree); result[0] = p[0] + scalar; for (unsigned int i = 1; i <= degree; ++i) { result[i] = p[i]; } return result; } template Polynomial1 operator+(Real scalar, Polynomial1 const& p) { unsigned int const degree = p.GetDegree(); Polynomial1 result(degree); result[0] = p[0] + scalar; for (unsigned int i = 1; i <= degree; ++i) { result[i] = p[i]; } return result; } template Polynomial1 operator-(Polynomial1 const& p, Real scalar) { unsigned int const degree = p.GetDegree(); Polynomial1 result(degree); result[0] = p[0] - scalar; for (unsigned int i = 1; i <= degree; ++i) { result[i] = p[i]; } return result; } template Polynomial1 operator-(Real scalar, Polynomial1 const& p) { unsigned int const degree = p.GetDegree(); Polynomial1 result(degree); result[0] = scalar - p[0]; for (unsigned int i = 1; i <= degree; ++i) { result[i] = -p[i]; } return result; } template Polynomial1 operator*(Polynomial1 const& p, Real scalar) { unsigned int const degree = p.GetDegree(); Polynomial1 result(degree); for (unsigned int i = 0; i <= degree; ++i) { result[i] = scalar * p[i]; } return result; } template Polynomial1 operator*(Real scalar, Polynomial1 const& p) { unsigned int const degree = p.GetDegree(); Polynomial1 result(degree); for (unsigned int i = 0; i <= degree; ++i) { result[i] = scalar * p[i]; } return result; } template Polynomial1 operator/(Polynomial1 const& p, Real scalar) { LogAssert(scalar != (Real)0, "Division by zero."); unsigned int const degree = p.GetDegree(); Real invScalar = (Real)1 / scalar; Polynomial1 result(degree); for (unsigned int i = 0; i <= degree; ++i) { result[i] = invScalar * p[i]; } return result; } template Polynomial1& operator+=(Polynomial1& p0, Polynomial1 const& p1) { p0 = p0 + p1; return p0; } template Polynomial1& operator-=(Polynomial1& p0, Polynomial1 const& p1) { p0 = p0 - p1; return p0; } template Polynomial1& operator*=(Polynomial1& p0, Polynomial1 const& p1) { p0 = p0 * p1; return p0; } template Polynomial1& operator+=(Polynomial1& p, Real scalar) { p[0] += scalar; return p; } template Polynomial1& operator-=(Polynomial1& p, Real scalar) { p[0] -= scalar; return p; } template Polynomial1& operator*=(Polynomial1& p, Real scalar) { p = p * scalar; return p; } template Polynomial1 & operator/=(Polynomial1& p, Real scalar) { p = p / scalar; return p; } }