/*
* This file is part of libgim.
*
* libgim is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see .
*
* Copyright 2015 Danny Robson
*/
#include "polynomial.hpp"
#include "maths.hpp"
#include
#include
//-----------------------------------------------------------------------------
namespace util { namespace polynomial {
template <>
std::array
solve (std::array coeff)
{
const float a = coeff[0];
const float b = coeff[1];
if (almost_zero (a))
return { std::numeric_limits::quiet_NaN () };
return { -b / a };
}
} }
//-----------------------------------------------------------------------------
namespace util { namespace polynomial {
template <>
std::array
solve (std::array coeff)
{
const float a = coeff[0];
const float b = coeff[1];
const float c = coeff[2];
if (almost_zero (a)) {
auto s = solve<2> ({b, c});
return { s[0], std::numeric_limits::quiet_NaN () };
}
auto d = std::sqrt (pow2 (b) - 4 * a * c);
return { (-b - d) / (2 * a),
(-b + d) / (2 * a) };
}
} }
//-----------------------------------------------------------------------------
// From graphics gems: http://tog.acm.org/resources/GraphicsGems/gemsiv/vec_mat/ray/solver.c
namespace util { namespace polynomial {
template <>
std::array
solve (std::array coeffs)
{
const float _a = coeffs[0];
const float _b = coeffs[1];
const float _c = coeffs[2];
const float _d = coeffs[3];
if (almost_zero (_a)) {
auto s = solve<3> ({_b, _c, _d});
return {s[0], s[1], std::numeric_limits::quiet_NaN () };
}
std::array s = {
std::numeric_limits::quiet_NaN (),
std::numeric_limits::quiet_NaN (),
std::numeric_limits::quiet_NaN ()
};
// Normalise to x^3 + ax^2 + bx + c = 0
const float a = _b / _a;
const float b = _c / _a;
const float c = _d / _a;
// Substituate x = y - a / 3 to eliminate the quadric. Now: x^3 + px + q = 0
float p = (-a * a / 3.f + b) / 3.f;
float q = (2 * a * a * a / 27.f - a * b /3.f + c) / 2.f;
// Solve using Cardano's method
float D = q * q + p * p * p;
if (almost_zero (D))
{
if (almost_zero (q)) {
s[0] = 0.f;
} else {
float u = std::cbrt (-q);
s[0] = 2 * u;
s[1] = -u;
}
} else if (D < 0) {
float phi = std::acos (-q / std::sqrt (-p * p * p)) / 3.f;
float t = 2 * std::sqrt (-p);
s[0] = t * std::cos (phi);
s[1] = -t * std::cos (phi + PI_f / 3.f);
s[2] = -t * std::cos (phi - PI_f / 3.f);
} else {
float u = std::cbrt (std::sqrt (D) + std::fabs (q));
if (q > 0.f)
s[0] = -u + p / u;
else
s[0] = u - p / u;
}
// Resubstitute a / 3 from above
float sub = a / 3.f;
for (auto &i: s)
i -= sub;
// Run an iteration of Newtons method to make the results slightly
// more accurate, they're a little loose straight out of the bat.
float da = 3;
float db = 2 * a;
float dc = b;
for (auto &i: s) {
float deriv = da * i * i + db * i + dc;
if (almost_zero (deriv))
continue;
i = i - eval (coeffs, i) / deriv;
}
return s;
}
} }
//-----------------------------------------------------------------------------
template
float
util::polynomial::eval (const std::array coeffs, const float x)
{
float x_ = 1.f;
float sum = 0.f;
for (size_t i = 0; i < S; ++i) {
sum += coeffs[S-i-1] * x_;
x_ *= x;
}
return sum;
}
//-----------------------------------------------------------------------------
template float util::polynomial::eval (std::array, float);
template float util::polynomial::eval (std::array, float);
template float util::polynomial::eval (std::array, float);
template float util::polynomial::eval (std::array, float);