|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#ifndef TCG_POLY_OPS_H
|
|
Toshihiro Shimizu |
890ddd |
#define TCG_POLY_OPS_H
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// tcg includes
|
|
Toshihiro Shimizu |
890ddd |
#include "macros.h"
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\file tcg_poly_ops.h
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\brief This file contains useful functions for operating with polynomials.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//*******************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
// Polynomial Operations
|
|
Toshihiro Shimizu |
890ddd |
//*******************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
namespace tcg
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Contains useful functions for operating with polynomials.
|
|
Toshihiro Shimizu |
890ddd |
namespace poly_ops
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Evaluates a polynomial using Horner's algorithm
|
|
Toshihiro Shimizu |
890ddd |
\return The value of the input polynomial at the specified parameter
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename scalar=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
Scalar evaluate(
|
|
Toshihiro Shimizu |
890ddd |
const Scalar poly[], //!< Coefficients of the input polynomial, indexed by degree
|
|
Toshihiro Shimizu |
890ddd |
int deg, //!< Degree of the polynomial function
|
|
Toshihiro Shimizu |
890ddd |
Scalar x) //!< Parameter the polynomial will be evaluated on
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
// ((poly[deg] * x) + poly[deg-1]) * x + poly[deg - 2] + ...
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Scalar value = poly[deg];
|
|
Toshihiro Shimizu |
890ddd |
for (int d = deg - 1; d >= 0; --d)
|
|
Toshihiro Shimizu |
890ddd |
value = value * x + poly[d];
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
return value;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Reduces the degree of the input polynomial, discarding all leading coefficients
|
|
Toshihiro Shimizu |
890ddd |
whose absolute value is below the specified tolerance threshold.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename scalar=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void reduceDegree(
|
|
Toshihiro Shimizu |
890ddd |
const Scalar poly[], //!< Input polynomial to be reduced.
|
|
Toshihiro Shimizu |
890ddd |
int °, //!< Input/output polynomial degree.
|
|
Toshihiro Shimizu |
890ddd |
Scalar tolerance //!< Coefficients threshold to reduce the polynomial with.
|
|
Toshihiro Shimizu |
890ddd |
)
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
while ((deg > 0) && (std::abs(poly[deg]) < tolerance))
|
|
Toshihiro Shimizu |
890ddd |
--deg;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Adds two polynomials and returns the sum.
|
|
Toshihiro Shimizu |
890ddd |
\remark The supplied polynomials can actually be the same.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename a,="" b,="" c,="" deg="" int="" typename=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void add(
|
|
Toshihiro Shimizu |
890ddd |
const A(&poly1)[deg], //!< First polynomial addendum.
|
|
Toshihiro Shimizu |
890ddd |
const B(&poly2)[deg], //!< Second polynomial addendum.
|
|
Toshihiro Shimizu |
890ddd |
C(&result)[deg]) //!< Resulting sum.
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
for (int d = 0; d != deg; ++d)
|
|
Toshihiro Shimizu |
890ddd |
result[d] = poly1[d] + poly2[d];
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Subtracts two polynomials /p poly1 and \p poly2 and returns the
|
|
Toshihiro Shimizu |
890ddd |
difference <tt>poly1 - poly2</tt>.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename a,="" b,="" c,="" deg="" int="" typename=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void sub(
|
|
Toshihiro Shimizu |
890ddd |
const A(&poly1)[deg], //!< First polynomial addendum.
|
|
Toshihiro Shimizu |
890ddd |
const B(&poly2)[deg], //!< Second polynomial addendum.
|
|
Toshihiro Shimizu |
890ddd |
C(&result)[deg]) //!< Resulting difference.
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
for (int d = 0; d != deg; ++d)
|
|
Toshihiro Shimizu |
890ddd |
result[d] = poly1[d] - poly2[d];
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Multiplies two polynomials into a polynomial whose degree is the
|
|
Toshihiro Shimizu |
890ddd |
\a sum of the multiplicands' degrees.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\warning The resulting polynomial is currently not allowed to be one
|
|
Toshihiro Shimizu |
890ddd |
of the multiplicands.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename a,="" b,="" c,="" deg1,="" deg2,="" degr="" int="" typename=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void mul(
|
|
Toshihiro Shimizu |
890ddd |
const A(&poly1)[deg1], //!< First multiplicand.
|
|
Toshihiro Shimizu |
890ddd |
const B(&poly2)[deg2], //!< Second multiplicand.
|
|
Toshihiro Shimizu |
890ddd |
C(&result)[degR]) //!< Resulting polynomial.
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
TCG_STATIC_ASSERT(degR == deg1 + deg2 - 1);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (int a = 0; a != deg1; ++a) {
|
|
Toshihiro Shimizu |
890ddd |
for (int b = 0; b != deg2; ++b)
|
|
Toshihiro Shimizu |
890ddd |
result[a + b] += poly1[a] * poly2[b];
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Calculates the chaining <tt>poly1 o poly2</tt> of two given polynomial
|
|
Toshihiro Shimizu |
890ddd |
\p poly1 and \p poly2.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\warning The resulting polynomial is currently not allowed to be one
|
|
Toshihiro Shimizu |
890ddd |
of the multiplicands.
|
|
Toshihiro Shimizu |
890ddd |
\warning This function is still \b untested.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename a,="" b,="" c,="" deg1,="" deg2,="" degr="" int="" typename=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void chain(
|
|
Toshihiro Shimizu |
890ddd |
const A(&poly1)[deg1], //!< First polynomial.
|
|
Toshihiro Shimizu |
890ddd |
const B(&poly2)[deg2], //!< Second polynomial.
|
|
Toshihiro Shimizu |
890ddd |
C(&result)[degR]) //!< Resulting polynomial.
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
for (int a = 0; a != deg1; ++a) {
|
|
Toshihiro Shimizu |
890ddd |
B pow[degR][2] = {{}};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Build poly2 powers
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
std::copy(poly2, poly2 + deg2, pow[1]);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (int p = 1; p < a; ++p)
|
|
Toshihiro Shimizu |
890ddd |
poly_mul(pow[p % 2], poly2, pow[(p + 1) % 2]);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
B(&pow_add)
|
|
Toshihiro Shimizu |
890ddd |
[degR] = pow[a % 2];
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// multiply by poly1[a]
|
|
Toshihiro Shimizu |
890ddd |
C addendum[degR];
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (int c = 0; c != degR; ++c)
|
|
Toshihiro Shimizu |
890ddd |
addendum[c] = poly1[c] * pow_add[c];
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
poly_add(addendum, result);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename a,="" b,="" deg,="" degr="" int="" typename=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void derivative(
|
|
Toshihiro Shimizu |
890ddd |
const A(&poly)[deg], //!< Polynomial to be derived.
|
|
Toshihiro Shimizu |
890ddd |
B(&result)[degR]) //!< Resulting derivative polynomial.
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
TCG_STATIC_ASSERT(degR == deg - 1);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (int c = 1; c != deg; ++c)
|
|
Toshihiro Shimizu |
890ddd |
result[c - 1] = c * poly[c];
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Solves the 1st degree equation: $c[1] t + c[0] = 0$
|
|
Toshihiro Shimizu |
890ddd |
\return The number of solutions found under the specified divide-by tolerance
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename scalar=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
inline unsigned int solve_1(
|
|
Toshihiro Shimizu |
890ddd |
Scalar c[2], //!< Polynomial coefficients array
|
|
Toshihiro Shimizu |
890ddd |
Scalar s[1], //!< Solutions array
|
|
Toshihiro Shimizu |
890ddd |
Scalar tol = 0) //!< Leading coefficient tolerance, the equation has no solution
|
|
Toshihiro Shimizu |
890ddd |
//! if the leading coefficient is below this threshold
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
if (std::abs(c[1]) <= tol)
|
|
Toshihiro Shimizu |
890ddd |
return 0;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
s[0] = -c[0] / c[1];
|
|
Toshihiro Shimizu |
890ddd |
return 1;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Solves the 2nd degree equation: $c[2] t^2 + c[1] t + c[0] = 0$
|
|
Toshihiro Shimizu |
890ddd |
\return The number of #real# solutions found under the specified divide-by tolerance
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\remark The returned solutions are sorted, with $s[0] <= s[1]$
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename scalar=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
unsigned int solve_2(
|
|
Toshihiro Shimizu |
890ddd |
Scalar c[3], //!< Polynomial coefficients array
|
|
Toshihiro Shimizu |
890ddd |
Scalar s[2], //!< Solutions array
|
|
Toshihiro Shimizu |
890ddd |
Scalar tol = 0) //!< Leading coefficient tolerance, the equation has no solution
|
|
Toshihiro Shimizu |
890ddd |
//! if the leading coefficient is below this threshold
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
if (std::abs(c[2]) <= tol)
|
|
Toshihiro Shimizu |
890ddd |
return solve_1(c, s, tol); // Reduces to first degree
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Scalar nc[2] = {c[0] / c[2], c[1] / (c[2] + c[2])}; // NOTE: nc[1] gets further divided by 2
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Scalar delta = nc[1] * nc[1] - nc[0];
|
|
Toshihiro Shimizu |
890ddd |
if (delta < 0)
|
|
Toshihiro Shimizu |
890ddd |
return 0;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
delta = sqrt(delta);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
s[0] = -delta - nc[1];
|
|
Toshihiro Shimizu |
890ddd |
s[1] = delta - nc[1];
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
return 2;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//-------------------------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Solves the 3rd degree equation: $c[3]t^3 + c[2] t^2 + c[1] t + c[0] = 0$
|
|
Toshihiro Shimizu |
890ddd |
\return The number of #real# solutions found under the specified divide-by tolerance
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\remark The returned solutions are sorted, with $s[0] <= s[1] <= s[2]$
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
template <typename scalar=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
unsigned int solve_3(
|
|
Toshihiro Shimizu |
890ddd |
Scalar c[4], //!< Polynomial coefficients array
|
|
Toshihiro Shimizu |
890ddd |
Scalar s[3], //!< Solutions array
|
|
Toshihiro Shimizu |
890ddd |
Scalar tol = 0) //!< Leading coefficient tolerance, the equation is reduced to 2nd degree
|
|
Toshihiro Shimizu |
890ddd |
//! if the leading coefficient is below this threshold
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
if (std::abs(c[3]) <= tol)
|
|
Toshihiro Shimizu |
890ddd |
return solve_2(c, s, tol); // Reduces to second degree
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Scalar nc[3] = {c[0] / c[3], c[1] / c[3], c[2] / c[3]};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Scalar b2 = nc[2] * nc[2];
|
|
Toshihiro Shimizu |
890ddd |
Scalar p = nc[1] - b2 / 3;
|
|
Toshihiro Shimizu |
890ddd |
Scalar q = nc[2] * (b2 + b2 - 9 * nc[1]) / 27 + nc[0];
|
|
Toshihiro Shimizu |
890ddd |
Scalar p3 = p * p * p;
|
|
Toshihiro Shimizu |
890ddd |
Scalar d = q * q + 4 * p3 / 27;
|
|
Toshihiro Shimizu |
890ddd |
Scalar offset = -nc[2] / 3;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if (d >= 0) {
|
|
Toshihiro Shimizu |
890ddd |
// Single solution
|
|
Toshihiro Shimizu |
890ddd |
Scalar z = sqrt(d);
|
|
Toshihiro Shimizu |
890ddd |
Scalar u = (-q + z) / 2;
|
|
Toshihiro Shimizu |
890ddd |
Scalar v = (-q - z) / 2;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
u = (u < 0) ? -pow(-u, 1 / Scalar(3)) : pow(u, 1 / Scalar(3));
|
|
Toshihiro Shimizu |
890ddd |
v = (v < 0) ? -pow(-v, 1 / Scalar(3)) : pow(v, 1 / Scalar(3));
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
s[0] = offset + u + v;
|
|
Toshihiro Shimizu |
890ddd |
return 1;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
assert(p3 < 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Scalar u = sqrt(-p / Scalar(3));
|
|
Toshihiro Shimizu |
890ddd |
Scalar v = acos(-sqrt(-27 / p3) * q / Scalar(2)) / Scalar(3);
|
|
Toshihiro Shimizu |
890ddd |
Scalar m = cos(v), n = sin(v) * 1.7320508075688772935274463415059; // sqrt(3)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
s[0] = offset - u * (n + m);
|
|
Toshihiro Shimizu |
890ddd |
s[1] = offset + u * (n - m);
|
|
Toshihiro Shimizu |
890ddd |
s[2] = offset + u * (m + m);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
assert(s[0] <= s[1] && s[1] <= s[2]);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
return 3;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
} // namespace tcg::poly_ops
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#endif // TCG_POLY_OPS_H
|