// (C) Copyright John Maddock 2006. // 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) #include #include #include #include #include #include #include #include #include #include #include #include "test_gamma_hooks.hpp" template void print_test_result(const boost::math::tools::test_result& result, const Seq& worst, int row, const char* name, const char* test) { using namespace std; T eps = pow(T(2), 1-boost::math::tools::digits()); std::cout << setprecision(4); std::cout << test << "(" << name << ") Max = " << (result.stat.max)()/eps << " RMS Mean =" << result.stat.rms()/eps << "\n worst case at row: " << row << "\n { "; for(unsigned i = 0; i < worst.size(); ++i) { if(i) std::cout << ", "; std::cout << worst[i]; } std::cout << " }" << std::endl; } template void do_test_gamma(const T& data, const char* type_name, const char* test_name) { typedef typename T::value_type row_type; typedef typename row_type::value_type value_type; typedef value_type (*pg)(value_type); pg funcp = boost::math::tgamma; boost::math::tools::test_result result; std::cout << "Testing " << test_name << " with type " << type_name << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; // // test tgamma against data: // result = boost::math::tools::test( data, boost::lambda::bind(funcp, boost::lambda::ret(boost::lambda::_1[0])), boost::lambda::ret(boost::lambda::_1[1])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::tgamma"); #ifdef TEST_OTHER if(::boost::is_floating_point::value){ funcp = other::tgamma; result = boost::math::tools::test( data, boost::lambda::bind(funcp, boost::lambda::ret(boost::lambda::_1[0])), boost::lambda::ret(boost::lambda::_1[1])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "other::tgamma"); } #endif // // test lgamma against data: // funcp = boost::math::lgamma; result = boost::math::tools::test( data, boost::lambda::bind(funcp, boost::lambda::ret(boost::lambda::_1[0])), boost::lambda::ret(boost::lambda::_1[2])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::lgamma"); #ifdef TEST_OTHER if(::boost::is_floating_point::value){ funcp = other::lgamma; result = boost::math::tools::test( data, boost::lambda::bind(funcp, boost::lambda::ret(boost::lambda::_1[0])), boost::lambda::ret(boost::lambda::_1[2])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "other::lgamma"); } #endif std::cout << std::endl; } template void do_test_gammap1m1(const T& data, const char* type_name, const char* test_name) { typedef typename T::value_type row_type; typedef typename row_type::value_type value_type; typedef value_type (*pg)(value_type); pg funcp = boost::math::tgammap1m1; boost::math::tools::test_result result; std::cout << "Testing " << test_name << " with type " << type_name << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; // // test tgammap1m1 against data: // result = boost::math::tools::test( data, boost::lambda::bind(funcp, boost::lambda::ret(boost::lambda::_1[0])), boost::lambda::ret(boost::lambda::_1[1])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::tgammap1m1"); std::cout << std::endl; } template void do_test_gamma_2(const T& data, const char* type_name, const char* test_name) { typedef typename T::value_type row_type; typedef typename row_type::value_type value_type; typedef value_type (*pg)(value_type, value_type); pg funcp = boost::math::tgamma; using namespace boost::lambda; boost::math::tools::test_result result; std::cout << "Testing " << test_name << " with type " << type_name << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; // // test tgamma(T, T) against data: // if(data[0][2] > 0) { result = boost::math::tools::test( data, bind(funcp, ret(_1[0]), ret(_1[1])), ret(_1[2])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::tgamma"); // // test tgamma_lower(T, T) against data: // funcp = boost::math::tgamma_lower; result = boost::math::tools::test( data, bind(funcp, ret(_1[0]), ret(_1[1])), ret(_1[4])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::tgamma_lower"); } // // test gamma_Q(T, T) against data: // funcp = boost::math::gamma_Q; result = boost::math::tools::test( data, bind(funcp, ret(_1[0]), ret(_1[1])), ret(_1[3])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::gamma_Q"); #if defined(TEST_CEPHES) || defined(TEST_GSL) // // test other gamma_Q(T, T) against data: // if(boost::is_floating_point::value) { funcp = other::gamma_Q; result = boost::math::tools::test( data, bind(funcp, ret(_1[0]), ret(_1[1])), ret(_1[3])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "other::gamma_Q"); } #endif // // test gamma_P(T, T) against data: // funcp = boost::math::gamma_P; result = boost::math::tools::test( data, bind(funcp, ret(_1[0]), ret(_1[1])), ret(_1[5])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::gamma_P"); #if defined(TEST_CEPHES) || defined(TEST_GSL) // // test other gamma_P(T, T) against data: // if(boost::is_floating_point::value) { funcp = other::gamma_P; result = boost::math::tools::test( data, bind(funcp, ret(_1[0]), ret(_1[1])), ret(_1[5])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "other::gamma_P"); } #endif // // test gamma_P_inv(T, T) against data: // using namespace std; typedef typename T::value_type row_type; typedef typename row_type::value_type value_type; value_type precision = static_cast(ldexp(1.0, 1-boost::math::tools::digits()/2)) * 100; if(boost::math::tools::digits() < 50) precision = 1; // 1% or two decimal digits, all we can hope for when the input is truncated for(unsigned i = 0; i < data.size(); ++i) { // // These inverse tests are thrown off if the output of the // incomplete gamma is too close to 1: basically there is insuffient // information left in the value we're using as input to the inverse // to be able to get back to the original value. // if(data[i][5] == 0) BOOST_CHECK_EQUAL(boost::math::gamma_P_inv(data[i][0], data[i][5]), value_type(0)); else if((1 - data[i][5] > 0.001) && (fabs(data[i][5]) >= boost::math::tools::min_value())) { value_type inv = boost::math::gamma_P_inv(data[i][0], data[i][5]); BOOST_CHECK_CLOSE(data[i][1], inv, precision); } else if(1 == data[i][5]) BOOST_CHECK_EQUAL(boost::math::gamma_P_inv(data[i][0], data[i][5]), boost::math::tools::max_value()); else { // not enough bits in our input to get back to x, but we should be in // the same ball park: value_type inv = boost::math::gamma_P_inv(data[i][0], data[i][5]); BOOST_CHECK_CLOSE(data[i][1], inv, 100000); } if(data[i][3] == 0) BOOST_CHECK_EQUAL(boost::math::gamma_Q_inv(data[i][0], data[i][3]), boost::math::tools::max_value()); else if((1 - data[i][3] > 0.001) && (fabs(data[i][3]) >= boost::math::tools::min_value())) { value_type inv = boost::math::gamma_Q_inv(data[i][0], data[i][3]); BOOST_CHECK_CLOSE(data[i][1], inv, precision); } else if(1 == data[i][3]) BOOST_CHECK_EQUAL(boost::math::gamma_Q_inv(data[i][0], data[i][3]), value_type(0)); else { // not enough bits in our input to get back to x, but we should be in // the same ball park: value_type inv = boost::math::gamma_Q_inv(data[i][0], data[i][3]); BOOST_CHECK_CLOSE(data[i][1], inv, 100); } } std::cout << std::endl; } template void do_test_gamma_inv(const T& data, const char* type_name, const char* test_name) { typedef typename T::value_type row_type; typedef typename row_type::value_type value_type; typedef value_type (*pg)(value_type, value_type); pg funcp = boost::math::gamma_P_inv; using namespace boost::lambda; boost::math::tools::test_result result; std::cout << "Testing " << test_name << " with type " << type_name << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; // // test gamma_P_inv(T, T) against data: // result = boost::math::tools::test( data, bind(funcp, ret(_1[0]), ret(_1[1])), ret(_1[2])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::gamma_P_inv"); // // test gamma_Q_inv(T, T) against data: // funcp = boost::math::gamma_Q_inv; result = boost::math::tools::test( data, bind(funcp, ret(_1[0]), ret(_1[1])), ret(_1[3])); print_test_result(result, data[result.worst_case], result.worst_case, type_name, "boost::math::gamma_Q_inv"); } template void test_gamma(T, const char* name) { // // The actual test data is rather verbose, so it's in a separate file // // The contents are as follows, each row of data contains // three items, input value, gamma and lgamma: // // gamma and lgamma at integer and half integer values: // boost::array, N> factorials; // // gamma and lgamma for z near 0: // boost::array, N> near_0; // // gamma and lgamma for z near 1: // boost::array, N> near_1; // // gamma and lgamma for z near 2: // boost::array, N> near_2; // // gamma and lgamma for z near -10: // boost::array, N> near_m10; // // gamma and lgamma for z near -55: // boost::array, N> near_m55; // // The last two cases are chosen more or less at random, // except that one is even and the other odd, and both are // at negative poles. The data near zero also tests near // a pole, the data near 1 and 2 are to probe lgamma as // the result -> 0. // # include "test_gamma_data.ipp" do_test_gamma(factorials, name, "factorials"); do_test_gamma(near_0, name, "near 0"); do_test_gamma(near_1, name, "near 1"); do_test_gamma(near_2, name, "near 2"); do_test_gamma(near_m10, name, "near -10"); do_test_gamma(near_m55, name, "near -55"); // // And now tgammap1m1 which computes gamma(1+dz)-1: // do_test_gammap1m1(gammap1m1_data, name, "tgammap1m1(dz)"); // // Now the data for the incomplete gamma function, each // row has the following entries: // Parameter a, parameter z, // Expected tgamma(a, z), Expected gamma_Q(a, z) // Expected tgamma_lower(a, z), Expected gamma_P(a, z) // # include "igamma_med_data.ipp" do_test_gamma_2(igamma_med_data, name, "tgamma(a, z) medium values"); # include "igamma_small_data.ipp" do_test_gamma_2(igamma_small_data, name, "tgamma(a, z) small values"); # include "igamma_big_data.ipp" do_test_gamma_2(igamma_big_data, name, "tgamma(a, z) large values"); # include "gamma_inv_data.ipp" do_test_gamma_inv(gamma_inv_data, name, "incomplete gamma inverse(a, z) medium values"); # include "gamma_inv_big_data.ipp" do_test_gamma_inv(gamma_inv_big_data, name, "incomplete gamma inverse(a, z) large values"); # include "gamma_inv_small_data.ipp" do_test_gamma_inv(gamma_inv_small_data, name, "incomplete gamma inverse(a, z) small values"); } template void test_spots(T) { // // basic sanity checks, tolerance is 10 decimal places expressed as a percentage: // T tolerance = std::pow(10.0, -8); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(3.5)), static_cast(3.3233509704478425512), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(0.1)), static_cast(9.5135076986687318363), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(-0.1)), static_cast(-10.686287021193193549), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(-3.2)), static_cast(0.68905641200597974292), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(-52.001)), static_cast(-1.2349010280547181826e-65), tolerance); int sign = 1; BOOST_CHECK_CLOSE(::boost::math::lgamma(static_cast(3.5), &sign), static_cast(1.2009736023470742248208170321516), tolerance); BOOST_CHECK(sign == 1); BOOST_CHECK_CLOSE(::boost::math::lgamma(static_cast(0.1), &sign), static_cast(2.2527126517342059598704913469682), tolerance); BOOST_CHECK(sign == 1); BOOST_CHECK_CLOSE(::boost::math::lgamma(static_cast(-0.1), &sign), static_cast(2.3689613327287886552092298040686), tolerance); BOOST_CHECK(sign == -1); BOOST_CHECK_CLOSE(::boost::math::lgamma(static_cast(-3.2), &sign), static_cast(-0.37243213612996769536089336752751), tolerance); BOOST_CHECK(sign == 1); BOOST_CHECK_CLOSE(::boost::math::lgamma(static_cast(-52.001), &sign), static_cast(-149.4570402169712972001218882919), tolerance); BOOST_CHECK(sign == -1); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(5), static_cast(1)), static_cast(23.912163676143750904), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(5), static_cast(5)), static_cast(10.571838841565097875), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(5), static_cast(10)), static_cast(0.70206451384706574415), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(5), static_cast(100)), static_cast(3.8734332808745531497e-36), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(0.5), static_cast(0.5)), static_cast(0.562418231594407124279494957302), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(0.5), static_cast(0.9)), static_cast(0.318532103604121098738593603904), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(0.5), static_cast(5)), static_cast(0.00277460326041280931949083572726), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(0.5), static_cast(100)), static_cast(3.70174786040827892025356644813e-45), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma_lower(static_cast(5), static_cast(1)), static_cast(0.087836323856249096291), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma_lower(static_cast(5), static_cast(5)), static_cast(13.428161158434902125), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma_lower(static_cast(5), static_cast(10)), static_cast(23.297935486152934256), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma_lower(static_cast(5), static_cast(100)), static_cast(24.000000000000000000), tolerance); BOOST_CHECK_CLOSE(::boost::math::gamma_Q(static_cast(5), static_cast(1)), static_cast(0.996340153172656287654543544187), tolerance); BOOST_CHECK_CLOSE(::boost::math::gamma_Q(static_cast(5), static_cast(5)), static_cast(0.440493285065212411442581665663), tolerance); BOOST_CHECK_CLOSE(::boost::math::gamma_Q(static_cast(5), static_cast(10)), static_cast(0.0292526880769610726727661331928), tolerance); BOOST_CHECK_CLOSE(::boost::math::gamma_Q(static_cast(5), static_cast(100)), static_cast(1.61393053369773047904057392250e-37), tolerance); BOOST_CHECK_CLOSE(::boost::math::gamma_P(static_cast(5), static_cast(1)), static_cast(0.00365984682734371234545645581271), tolerance); BOOST_CHECK_CLOSE(::boost::math::gamma_P(static_cast(5), static_cast(5)), static_cast(0.559506714934787588557418334337), tolerance); BOOST_CHECK_CLOSE(::boost::math::gamma_P(static_cast(5), static_cast(10)), static_cast(0.970747311923038927327233866807), tolerance); BOOST_CHECK_CLOSE(::boost::math::gamma_P(static_cast(5), static_cast(100)), static_cast(1.00000000000000000000000000000), tolerance); //BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(5), static_cast(1), static_cast(1.5)), static_cast(0.35798614547512708694), tolerance); //BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(5), static_cast(3), static_cast(6)), static_cast(12.724961860971380343), tolerance); //BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(5), static_cast(7), static_cast(9)), static_cast(2.8326711932871945905), tolerance); tolerance = boost::math::tools::epsilon() * 100 * 200; // 200 eps %. #if defined(__CYGWIN__) // some platforms long double is only reliably accurate to double precision: if(sizeof(T) == sizeof(long double)) tolerance = boost::math::tools::epsilon() * 100 * 200; // 200 eps %. #endif #if 0 // this is way too slow... for(T a = 0.5; a < 500; a += 2.5) { for(T p = 0.05; p < 1; p += 0.04) { T x = boost::math::gamma_P_inv(a, p); T p2 = boost::math::gamma_P(a, x); BOOST_CHECK_CLOSE(p2, p, tolerance); x = boost::math::gamma_Q_inv(a, p); p2 = boost::math::gamma_Q(a, x); BOOST_CHECK_CLOSE(p2, p, tolerance); } } #endif } int test_main(int, char* []) { //test_spots(0.0F); test_spots(0.0); test_spots(0.0L); test_spots(boost::math::concepts::real_concept(0.1)); test_gamma(0.1F, "float"); test_gamma(0.1, "double"); test_gamma(0.1L, "long double"); test_gamma(boost::math::concepts::real_concept(0.1), "real_concept"); return 0; }