// (C) Copyright Jeremy Murphy 2015. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Module build check for boost/math/tools/polynomial.hpp. Consuming the class // template and its free operators through `import boost.math;` confirms that // polynomial construction, coefficient access, Horner evaluation, the // arithmetic operators, quotient_remainder, pow, prime and integrate all // resolve from a module consumer (along with the std::vector / std::pair / // std::initializer_list machinery they rely on). Coefficients are stored in // ascending order, so index 0 is the constant term. The known-good expected // values are taken from test_polynomial.cpp (its double cases); the calculus // checks use mathematically-certain derivative and integral coefficients. // Only double and float are exercised. #ifndef BOOST_MATH_BUILD_MODULE #include #else import boost.math; #endif #include "math_unit_test.hpp" #ifndef BOOST_MATH_BUILD_MODULE #include #endif using boost::math::tools::polynomial; using boost::math::tools::quotient_remainder; // Construction, degree, indexing and Horner evaluation of // p(x) = 10 - 6x - 4x^2 + 3x^3. template void test_evaluation(const char* type_name) { std::cout << "Testing polynomial construction and evaluation on type " << type_name << std::endl; const polynomial a {Real(10), Real(-6), Real(-4), Real(3)}; CHECK_EQUAL(a.degree(), static_cast(3)); CHECK_EQUAL(Real(10), a[0]); CHECK_EQUAL(Real(-6), a[1]); CHECK_EQUAL(Real(-4), a[2]); CHECK_EQUAL(Real(3), a[3]); // Evaluate at exactly representable points. CHECK_ULP_CLOSE(Real(10), a(Real(0)), 3); // constant term CHECK_ULP_CLOSE(Real(3), a(Real(1)), 3); // 10 - 6 - 4 + 3 CHECK_ULP_CLOSE(Real(6), a.evaluate(Real(2)), 3); // 10 - 12 - 16 + 24 } // (10 - 6x - 4x^2 + 3x^3) + (-2 + x) = 8 - 5x - 4x^2 + 3x^3. template void test_addition(const char* type_name) { std::cout << "Testing polynomial addition on type " << type_name << std::endl; const polynomial a {Real(10), Real(-6), Real(-4), Real(3)}; const polynomial b {Real(-2), Real(1)}; const auto sum = a + b; CHECK_EQUAL(sum.degree(), static_cast(3)); CHECK_EQUAL(Real(8), sum[0]); CHECK_EQUAL(Real(-5), sum[1]); CHECK_EQUAL(Real(-4), sum[2]); CHECK_EQUAL(Real(3), sum[3]); } // a * a = 100 - 120x - 44x^2 + 108x^3 - 20x^4 - 24x^5 + 9x^6, and pow agrees. template void test_multiplication(const char* type_name) { std::cout << "Testing polynomial multiplication and pow on type " << type_name << std::endl; const polynomial a {Real(10), Real(-6), Real(-4), Real(3)}; const Real expected[7] {Real(100), Real(-120), Real(-44), Real(108), Real(-20), Real(-24), Real(9)}; const auto asq = a * a; CHECK_EQUAL(asq.degree(), static_cast(6)); for (std::size_t i {0}; i < asq.size(); ++i) { CHECK_EQUAL(expected[i], asq[i]); } // pow(a, 2) reproduces the product; pow(a, 0) is the multiplicative identity. const auto apow2 = boost::math::tools::pow(a, 2); for (std::size_t i {0}; i < apow2.size(); ++i) { CHECK_EQUAL(asq[i], apow2[i]); } const auto one = boost::math::tools::pow(a, 0); CHECK_EQUAL(one.degree(), static_cast(0)); CHECK_EQUAL(Real(1), one[0]); } // (3x^3 - 4x^2 - 6x + 10) / (x - 2) = 3x^2 + 2x - 2, remainder 6. template void test_division(const char* type_name) { std::cout << "Testing polynomial division on type " << type_name << std::endl; const polynomial a {Real(10), Real(-6), Real(-4), Real(3)}; const polynomial b {Real(-2), Real(1)}; const auto result = quotient_remainder(a, b); const polynomial& q = result.first; const polynomial& r = result.second; CHECK_EQUAL(q.degree(), static_cast(2)); CHECK_EQUAL(Real(-2), q[0]); CHECK_EQUAL(Real(2), q[1]); CHECK_EQUAL(Real(3), q[2]); CHECK_EQUAL(r.degree(), static_cast(0)); CHECK_EQUAL(Real(6), r[0]); // q * b + r reconstructs the dividend exactly. const auto reconstructed = q * b + r; for (std::size_t i {0}; i < a.size(); ++i) { CHECK_EQUAL(a[i], reconstructed[i]); } // Division by a scalar divides each coefficient; the remainder is zero. const auto a_over_3 = a / Real(3); CHECK_ULP_CLOSE(Real(10) / Real(3), a_over_3[0], 3); CHECK_ULP_CLOSE(Real(-2), a_over_3[1], 3); CHECK_ULP_CLOSE(Real(-4) / Real(3), a_over_3[2], 3); CHECK_ULP_CLOSE(Real(1), a_over_3[3], 3); const auto a_mod_3 = a % Real(3); CHECK_EQUAL(a_mod_3.size(), static_cast(0)); // A polynomial divided by itself is the multiplicative identity. const auto a_over_a = a / a; CHECK_EQUAL(a_over_a.degree(), static_cast(0)); CHECK_EQUAL(Real(1), a_over_a[0]); } // p(x) = 1 + x + x^2 + x^3 + x^4 has derivative 1 + 2x + 3x^2 + 4x^3 and, with // the constant chosen so P(0) = 0, integral coefficients 0, 1, 1/2, 1/3, 1/4, 1/5. template void test_calculus(const char* type_name) { std::cout << "Testing polynomial prime and integrate on type " << type_name << std::endl; const polynomial p {Real(1), Real(1), Real(1), Real(1), Real(1)}; const auto dp = p.prime(); CHECK_EQUAL(dp.degree(), static_cast(3)); for (std::size_t i {0}; i < dp.size(); ++i) { CHECK_EQUAL(Real(i + 1), dp[i]); } const auto P = p.integrate(); CHECK_EQUAL(Real(0), P[0]); for (std::size_t i {1}; i < P.size(); ++i) { CHECK_ULP_CLOSE(Real(1) / Real(i), P[i], 3); } } int main() { test_evaluation("float"); test_evaluation("double"); test_addition("float"); test_addition("double"); test_multiplication("float"); test_multiplication("double"); test_division("float"); test_division("double"); test_calculus("float"); test_calculus("double"); return boost::math::test::report_errors(); }