1af0d731bSPaulXiCao //===----------------------------------------------------------------------===// 2af0d731bSPaulXiCao // 3af0d731bSPaulXiCao // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4af0d731bSPaulXiCao // See https://llvm.org/LICENSE.txt for license information. 5af0d731bSPaulXiCao // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6af0d731bSPaulXiCao // 7af0d731bSPaulXiCao //===----------------------------------------------------------------------===// 8af0d731bSPaulXiCao 9af0d731bSPaulXiCao // UNSUPPORTED: c++03, c++11, c++14 10af0d731bSPaulXiCao 11af0d731bSPaulXiCao // <cmath> 12af0d731bSPaulXiCao 13af0d731bSPaulXiCao // double hermite(unsigned n, double x); 14af0d731bSPaulXiCao // float hermite(unsigned n, float x); 15af0d731bSPaulXiCao // long double hermite(unsigned n, long double x); 16af0d731bSPaulXiCao // float hermitef(unsigned n, float x); 17af0d731bSPaulXiCao // long double hermitel(unsigned n, long double x); 18af0d731bSPaulXiCao // template <class Integer> 19af0d731bSPaulXiCao // double hermite(unsigned n, Integer x); 20af0d731bSPaulXiCao 21af0d731bSPaulXiCao #include <array> 22af0d731bSPaulXiCao #include <cassert> 23af0d731bSPaulXiCao #include <cmath> 24af0d731bSPaulXiCao #include <limits> 25af0d731bSPaulXiCao #include <vector> 26af0d731bSPaulXiCao 27af0d731bSPaulXiCao #include "type_algorithms.h" 28af0d731bSPaulXiCao 29*f343fee8SZibi Sarbinowski template <class Real> 30*f343fee8SZibi Sarbinowski constexpr unsigned get_maximal_order() { 31*f343fee8SZibi Sarbinowski if constexpr (std::numeric_limits<Real>::is_iec559) 32*f343fee8SZibi Sarbinowski return 128; 33*f343fee8SZibi Sarbinowski else { // Workaround for z/OS HexFloat. 34*f343fee8SZibi Sarbinowski // Note |H_n(x)| < 10^75 for n < 39 and x in sample_points(). 35*f343fee8SZibi Sarbinowski static_assert(std::numeric_limits<Real>::max_exponent10 == 75); 36*f343fee8SZibi Sarbinowski return 39; 37*f343fee8SZibi Sarbinowski } 38*f343fee8SZibi Sarbinowski } 39af0d731bSPaulXiCao 40af0d731bSPaulXiCao template <class T> 41af0d731bSPaulXiCao std::array<T, 11> sample_points() { 42af0d731bSPaulXiCao return {-12.34, -7.42, -1.0, -0.5, -0.1, 0.0, 0.1, 0.5, 1.0, 5.67, 15.67}; 43af0d731bSPaulXiCao } 44af0d731bSPaulXiCao 45af0d731bSPaulXiCao template <class Real> 46af0d731bSPaulXiCao class CompareFloatingValues { 47af0d731bSPaulXiCao private: 48af0d731bSPaulXiCao Real abs_tol; 49af0d731bSPaulXiCao Real rel_tol; 50af0d731bSPaulXiCao 51af0d731bSPaulXiCao public: 52af0d731bSPaulXiCao CompareFloatingValues() { 53af0d731bSPaulXiCao abs_tol = []() -> Real { 54af0d731bSPaulXiCao if (std::is_same_v<Real, float>) 55af0d731bSPaulXiCao return 1e-5f; 56af0d731bSPaulXiCao else if (std::is_same_v<Real, double>) 57af0d731bSPaulXiCao return 1e-11; 58af0d731bSPaulXiCao else 59af0d731bSPaulXiCao return 1e-12l; 60af0d731bSPaulXiCao }(); 61af0d731bSPaulXiCao 62af0d731bSPaulXiCao rel_tol = abs_tol; 63af0d731bSPaulXiCao } 64af0d731bSPaulXiCao 65af0d731bSPaulXiCao bool operator()(Real result, Real expected) const { 66af0d731bSPaulXiCao if (std::isinf(expected) && std::isinf(result)) 67af0d731bSPaulXiCao return result == expected; 68af0d731bSPaulXiCao 69af0d731bSPaulXiCao if (std::isnan(expected) || std::isnan(result)) 70af0d731bSPaulXiCao return false; 71af0d731bSPaulXiCao 72af0d731bSPaulXiCao Real tol = abs_tol + std::abs(expected) * rel_tol; 73af0d731bSPaulXiCao return std::abs(result - expected) < tol; 74af0d731bSPaulXiCao } 75af0d731bSPaulXiCao }; 76af0d731bSPaulXiCao 77af0d731bSPaulXiCao // Roots are taken from 78af0d731bSPaulXiCao // Salzer, Herbert E., Ruth Zucker, and Ruth Capuano. 79af0d731bSPaulXiCao // Table of the zeros and weight factors of the first twenty Hermite 80af0d731bSPaulXiCao // polynomials. US Government Printing Office, 1952. 81af0d731bSPaulXiCao template <class T> 82af0d731bSPaulXiCao std::vector<T> get_roots(unsigned n) { 83af0d731bSPaulXiCao switch (n) { 84af0d731bSPaulXiCao case 0: 85af0d731bSPaulXiCao return {}; 86af0d731bSPaulXiCao case 1: 87af0d731bSPaulXiCao return {T(0)}; 88af0d731bSPaulXiCao case 2: 89af0d731bSPaulXiCao return {T(0.707106781186548)}; 90af0d731bSPaulXiCao case 3: 91af0d731bSPaulXiCao return {T(0), T(1.224744871391589)}; 92af0d731bSPaulXiCao case 4: 93af0d731bSPaulXiCao return {T(0.524647623275290), T(1.650680123885785)}; 94af0d731bSPaulXiCao case 5: 95af0d731bSPaulXiCao return {T(0), T(0.958572464613819), T(2.020182870456086)}; 96af0d731bSPaulXiCao case 6: 97af0d731bSPaulXiCao return {T(0.436077411927617), T(1.335849074013697), T(2.350604973674492)}; 98af0d731bSPaulXiCao case 7: 99af0d731bSPaulXiCao return {T(0), T(0.816287882858965), T(1.673551628767471), T(2.651961356835233)}; 100af0d731bSPaulXiCao case 8: 101af0d731bSPaulXiCao return {T(0.381186990207322), T(1.157193712446780), T(1.981656756695843), T(2.930637420257244)}; 102af0d731bSPaulXiCao case 9: 103af0d731bSPaulXiCao return {T(0), T(0.723551018752838), T(1.468553289216668), T(2.266580584531843), T(3.190993201781528)}; 104af0d731bSPaulXiCao case 10: 105af0d731bSPaulXiCao return { 106af0d731bSPaulXiCao T(0.342901327223705), T(1.036610829789514), T(1.756683649299882), T(2.532731674232790), T(3.436159118837738)}; 107af0d731bSPaulXiCao case 11: 108af0d731bSPaulXiCao return {T(0), 109af0d731bSPaulXiCao T(0.65680956682100), 110af0d731bSPaulXiCao T(1.326557084494933), 111af0d731bSPaulXiCao T(2.025948015825755), 112af0d731bSPaulXiCao T(2.783290099781652), 113af0d731bSPaulXiCao T(3.668470846559583)}; 114af0d731bSPaulXiCao 115af0d731bSPaulXiCao case 12: 116af0d731bSPaulXiCao return {T(0.314240376254359), 117af0d731bSPaulXiCao T(0.947788391240164), 118af0d731bSPaulXiCao T(1.597682635152605), 119af0d731bSPaulXiCao T(2.279507080501060), 120af0d731bSPaulXiCao T(3.020637025120890), 121af0d731bSPaulXiCao T(3.889724897869782)}; 122af0d731bSPaulXiCao 123af0d731bSPaulXiCao case 13: 124af0d731bSPaulXiCao return {T(0), 125af0d731bSPaulXiCao T(0.605763879171060), 126af0d731bSPaulXiCao T(1.220055036590748), 127af0d731bSPaulXiCao T(1.853107651601512), 128af0d731bSPaulXiCao T(2.519735685678238), 129af0d731bSPaulXiCao T(3.246608978372410), 130af0d731bSPaulXiCao T(4.101337596178640)}; 131af0d731bSPaulXiCao 132af0d731bSPaulXiCao case 14: 133af0d731bSPaulXiCao return {T(0.29174551067256), 134af0d731bSPaulXiCao T(0.87871378732940), 135af0d731bSPaulXiCao T(1.47668273114114), 136af0d731bSPaulXiCao T(2.09518325850772), 137af0d731bSPaulXiCao T(2.74847072498540), 138af0d731bSPaulXiCao T(3.46265693360227), 139af0d731bSPaulXiCao T(4.30444857047363)}; 140af0d731bSPaulXiCao 141af0d731bSPaulXiCao case 15: 142af0d731bSPaulXiCao return {T(0.00000000000000), 143af0d731bSPaulXiCao T(0.56506958325558), 144af0d731bSPaulXiCao T(1.13611558521092), 145af0d731bSPaulXiCao T(1.71999257518649), 146af0d731bSPaulXiCao T(2.32573248617386), 147af0d731bSPaulXiCao T(2.96716692790560), 148af0d731bSPaulXiCao T(3.66995037340445), 149af0d731bSPaulXiCao T(4.49999070730939)}; 150af0d731bSPaulXiCao 151af0d731bSPaulXiCao case 16: 152af0d731bSPaulXiCao return {T(0.27348104613815), 153af0d731bSPaulXiCao T(0.82295144914466), 154af0d731bSPaulXiCao T(1.38025853919888), 155af0d731bSPaulXiCao T(1.95178799091625), 156af0d731bSPaulXiCao T(2.54620215784748), 157af0d731bSPaulXiCao T(3.17699916197996), 158af0d731bSPaulXiCao T(3.86944790486012), 159af0d731bSPaulXiCao T(4.68873893930582)}; 160af0d731bSPaulXiCao 161af0d731bSPaulXiCao case 17: 162af0d731bSPaulXiCao return {T(0), 163af0d731bSPaulXiCao T(0.5316330013427), 164af0d731bSPaulXiCao T(1.0676487257435), 165af0d731bSPaulXiCao T(1.6129243142212), 166af0d731bSPaulXiCao T(2.1735028266666), 167af0d731bSPaulXiCao T(2.7577629157039), 168af0d731bSPaulXiCao T(3.3789320911415), 169af0d731bSPaulXiCao T(4.0619466758755), 170af0d731bSPaulXiCao T(4.8713451936744)}; 171af0d731bSPaulXiCao 172af0d731bSPaulXiCao case 18: 173af0d731bSPaulXiCao return {T(0.2582677505191), 174af0d731bSPaulXiCao T(0.7766829192674), 175af0d731bSPaulXiCao T(1.3009208583896), 176af0d731bSPaulXiCao T(1.8355316042616), 177af0d731bSPaulXiCao T(2.3862990891667), 178af0d731bSPaulXiCao T(2.9613775055316), 179af0d731bSPaulXiCao T(3.5737690684863), 180af0d731bSPaulXiCao T(4.2481178735681), 181af0d731bSPaulXiCao T(5.0483640088745)}; 182af0d731bSPaulXiCao 183af0d731bSPaulXiCao case 19: 184af0d731bSPaulXiCao return {T(0), 185af0d731bSPaulXiCao T(0.5035201634239), 186af0d731bSPaulXiCao T(1.0103683871343), 187af0d731bSPaulXiCao T(1.5241706193935), 188af0d731bSPaulXiCao T(2.0492317098506), 189af0d731bSPaulXiCao T(2.5911337897945), 190af0d731bSPaulXiCao T(3.1578488183476), 191af0d731bSPaulXiCao T(3.7621873519640), 192af0d731bSPaulXiCao T(4.4285328066038), 193af0d731bSPaulXiCao T(5.2202716905375)}; 194af0d731bSPaulXiCao 195af0d731bSPaulXiCao case 20: 196af0d731bSPaulXiCao return {T(0.2453407083009), 197af0d731bSPaulXiCao T(0.7374737285454), 198af0d731bSPaulXiCao T(1.2340762153953), 199af0d731bSPaulXiCao T(1.7385377121166), 200af0d731bSPaulXiCao T(2.2549740020893), 201af0d731bSPaulXiCao T(2.7888060584281), 202af0d731bSPaulXiCao T(3.347854567332), 203af0d731bSPaulXiCao T(3.9447640401156), 204af0d731bSPaulXiCao T(4.6036824495507), 205af0d731bSPaulXiCao T(5.3874808900112)}; 206af0d731bSPaulXiCao 207af0d731bSPaulXiCao default: // polynom degree n>20 is unsupported 208af0d731bSPaulXiCao assert(false); 209af0d731bSPaulXiCao return {T(-42)}; 210af0d731bSPaulXiCao } 211af0d731bSPaulXiCao } 212af0d731bSPaulXiCao 213af0d731bSPaulXiCao template <class Real> 214af0d731bSPaulXiCao void test() { 215*f343fee8SZibi Sarbinowski if constexpr ( 216*f343fee8SZibi Sarbinowski std::numeric_limits<Real>::has_quiet_NaN && 217*f343fee8SZibi Sarbinowski std::numeric_limits< 218*f343fee8SZibi Sarbinowski Real>::has_signaling_NaN) { // checks if NaNs are reported correctly (i.e. output == input for input == NaN) 219af0d731bSPaulXiCao using nl = std::numeric_limits<Real>; 220af0d731bSPaulXiCao for (Real NaN : {nl::quiet_NaN(), nl::signaling_NaN()}) 221*f343fee8SZibi Sarbinowski for (unsigned n = 0; n < get_maximal_order<Real>(); ++n) 222af0d731bSPaulXiCao assert(std::isnan(std::hermite(n, NaN))); 223af0d731bSPaulXiCao } 224af0d731bSPaulXiCao 225*f343fee8SZibi Sarbinowski if constexpr (std::numeric_limits<Real>::has_quiet_NaN && 226*f343fee8SZibi Sarbinowski std::numeric_limits< 227*f343fee8SZibi Sarbinowski Real>::has_signaling_NaN) { // simple sample points for n=0..127 should not produce NaNs. 228af0d731bSPaulXiCao for (Real x : sample_points<Real>()) 229*f343fee8SZibi Sarbinowski for (unsigned n = 0; n < get_maximal_order<Real>(); ++n) 230af0d731bSPaulXiCao assert(!std::isnan(std::hermite(n, x))); 231af0d731bSPaulXiCao } 232af0d731bSPaulXiCao 233af0d731bSPaulXiCao { // checks std::hermite(n, x) for n=0..5 against analytic polynoms 234af0d731bSPaulXiCao const auto h0 = [](Real) -> Real { return 1; }; 235af0d731bSPaulXiCao const auto h1 = [](Real y) -> Real { return 2 * y; }; 236af0d731bSPaulXiCao const auto h2 = [](Real y) -> Real { return 4 * y * y - 2; }; 237af0d731bSPaulXiCao const auto h3 = [](Real y) -> Real { return y * (8 * y * y - 12); }; 238af0d731bSPaulXiCao const auto h4 = [](Real y) -> Real { return (16 * std::pow(y, 4) - 48 * y * y + 12); }; 239af0d731bSPaulXiCao const auto h5 = [](Real y) -> Real { return y * (32 * std::pow(y, 4) - 160 * y * y + 120); }; 240af0d731bSPaulXiCao 241af0d731bSPaulXiCao for (Real x : sample_points<Real>()) { 242af0d731bSPaulXiCao const CompareFloatingValues<Real> compare; 243af0d731bSPaulXiCao assert(compare(std::hermite(0, x), h0(x))); 244af0d731bSPaulXiCao assert(compare(std::hermite(1, x), h1(x))); 245af0d731bSPaulXiCao assert(compare(std::hermite(2, x), h2(x))); 246af0d731bSPaulXiCao assert(compare(std::hermite(3, x), h3(x))); 247af0d731bSPaulXiCao assert(compare(std::hermite(4, x), h4(x))); 248af0d731bSPaulXiCao assert(compare(std::hermite(5, x), h5(x))); 249af0d731bSPaulXiCao } 250af0d731bSPaulXiCao } 251af0d731bSPaulXiCao 252af0d731bSPaulXiCao { // checks std::hermitef for bitwise equality with std::hermite(unsigned, float) 253af0d731bSPaulXiCao if constexpr (std::is_same_v<Real, float>) 254*f343fee8SZibi Sarbinowski for (unsigned n = 0; n < get_maximal_order<Real>(); ++n) 255af0d731bSPaulXiCao for (float x : sample_points<float>()) 256af0d731bSPaulXiCao assert(std::hermite(n, x) == std::hermitef(n, x)); 257af0d731bSPaulXiCao } 258af0d731bSPaulXiCao 259af0d731bSPaulXiCao { // checks std::hermitel for bitwise equality with std::hermite(unsigned, long double) 260af0d731bSPaulXiCao if constexpr (std::is_same_v<Real, long double>) 261*f343fee8SZibi Sarbinowski for (unsigned n = 0; n < get_maximal_order<Real>(); ++n) 262af0d731bSPaulXiCao for (long double x : sample_points<long double>()) 263af0d731bSPaulXiCao assert(std::hermite(n, x) == std::hermitel(n, x)); 264af0d731bSPaulXiCao } 265af0d731bSPaulXiCao 266af0d731bSPaulXiCao { // Checks if the characteristic recurrence relation holds: H_{n+1}(x) = 2x H_n(x) - 2n H_{n-1}(x) 267af0d731bSPaulXiCao for (Real x : sample_points<Real>()) { 268*f343fee8SZibi Sarbinowski for (unsigned n = 1; n < get_maximal_order<Real>() - 1; ++n) { 269af0d731bSPaulXiCao Real H_next = std::hermite(n + 1, x); 270af0d731bSPaulXiCao Real H_next_recurrence = 2 * (x * std::hermite(n, x) - n * std::hermite(n - 1, x)); 271af0d731bSPaulXiCao 272af0d731bSPaulXiCao if (std::isinf(H_next)) 273af0d731bSPaulXiCao break; 274af0d731bSPaulXiCao const CompareFloatingValues<Real> compare; 275af0d731bSPaulXiCao assert(compare(H_next, H_next_recurrence)); 276af0d731bSPaulXiCao } 277af0d731bSPaulXiCao } 278af0d731bSPaulXiCao } 279af0d731bSPaulXiCao 280af0d731bSPaulXiCao { // sanity checks: hermite polynoms need to change signs at (simple) roots. checked upto order n<=20. 281af0d731bSPaulXiCao 282af0d731bSPaulXiCao // root tolerance: must be smaller than the smallest difference between adjacent roots 283af0d731bSPaulXiCao Real tol = []() -> Real { 284af0d731bSPaulXiCao if (std::is_same_v<Real, float>) 285af0d731bSPaulXiCao return 1e-5f; 286af0d731bSPaulXiCao else if (std::is_same_v<Real, double>) 287af0d731bSPaulXiCao return 1e-9; 288af0d731bSPaulXiCao else 289af0d731bSPaulXiCao return 1e-10l; 290af0d731bSPaulXiCao }(); 291af0d731bSPaulXiCao 292af0d731bSPaulXiCao const auto is_sign_change = [tol](unsigned n, Real x) -> bool { 293af0d731bSPaulXiCao return std::hermite(n, x - tol) * std::hermite(n, x + tol) < 0; 294af0d731bSPaulXiCao }; 295af0d731bSPaulXiCao 296af0d731bSPaulXiCao for (unsigned n = 0; n <= 20u; ++n) { 297af0d731bSPaulXiCao for (Real x : get_roots<Real>(n)) { 298af0d731bSPaulXiCao // the roots are symmetric: if x is a root, so is -x 299af0d731bSPaulXiCao if (x > 0) 300af0d731bSPaulXiCao assert(is_sign_change(n, -x)); 301af0d731bSPaulXiCao assert(is_sign_change(n, x)); 302af0d731bSPaulXiCao } 303af0d731bSPaulXiCao } 304af0d731bSPaulXiCao } 305af0d731bSPaulXiCao 306*f343fee8SZibi Sarbinowski if constexpr (std::numeric_limits<Real>::has_infinity) { // check input infinity is handled correctly 307af0d731bSPaulXiCao Real inf = std::numeric_limits<Real>::infinity(); 308*f343fee8SZibi Sarbinowski for (unsigned n = 1; n < get_maximal_order<Real>(); ++n) { 309af0d731bSPaulXiCao assert(std::hermite(n, +inf) == inf); 310af0d731bSPaulXiCao assert(std::hermite(n, -inf) == ((n & 1) ? -inf : inf)); 311af0d731bSPaulXiCao } 312af0d731bSPaulXiCao } 313af0d731bSPaulXiCao 314*f343fee8SZibi Sarbinowski if constexpr (std::numeric_limits< 315*f343fee8SZibi Sarbinowski Real>::has_infinity) { // check: if overflow occurs that it is mapped to the correct infinity 316af0d731bSPaulXiCao if constexpr (std::is_same_v<Real, double>) { 317af0d731bSPaulXiCao // Q: Why only double? 318af0d731bSPaulXiCao // A: The numeric values (e.g. overflow threshold `n`) below are different for other types. 319af0d731bSPaulXiCao static_assert(sizeof(double) == 8); 320*f343fee8SZibi Sarbinowski for (unsigned n = 0; n < get_maximal_order<Real>(); ++n) { 321af0d731bSPaulXiCao // Q: Why n=111 and x=300? 322*f343fee8SZibi Sarbinowski // A: Both are chosen s.t. the first overlow occurs for some `n<get_maximal_order<Real>()`. 323af0d731bSPaulXiCao if (n < 111) { 324af0d731bSPaulXiCao assert(std::isfinite(std::hermite(n, +300.0))); 325af0d731bSPaulXiCao assert(std::isfinite(std::hermite(n, -300.0))); 326af0d731bSPaulXiCao } else { 327af0d731bSPaulXiCao double inf = std::numeric_limits<double>::infinity(); 328af0d731bSPaulXiCao assert(std::hermite(n, +300.0) == inf); 329af0d731bSPaulXiCao assert(std::hermite(n, -300.0) == ((n & 1) ? -inf : inf)); 330af0d731bSPaulXiCao } 331af0d731bSPaulXiCao } 332af0d731bSPaulXiCao } 333af0d731bSPaulXiCao } 334af0d731bSPaulXiCao } 335af0d731bSPaulXiCao 336af0d731bSPaulXiCao struct TestFloat { 337af0d731bSPaulXiCao template <class Real> 338af0d731bSPaulXiCao void operator()() { 339af0d731bSPaulXiCao test<Real>(); 340af0d731bSPaulXiCao } 341af0d731bSPaulXiCao }; 342af0d731bSPaulXiCao 343af0d731bSPaulXiCao struct TestInt { 344af0d731bSPaulXiCao template <class Integer> 345af0d731bSPaulXiCao void operator()() { 346af0d731bSPaulXiCao // checks that std::hermite(unsigned, Integer) actually wraps std::hermite(unsigned, double) 347*f343fee8SZibi Sarbinowski for (unsigned n = 0; n < get_maximal_order<double>(); ++n) 348af0d731bSPaulXiCao for (Integer x : {-42, -7, -5, -1, 0, 1, 5, 7, 42}) 349af0d731bSPaulXiCao assert(std::hermite(n, x) == std::hermite(n, static_cast<double>(x))); 350af0d731bSPaulXiCao } 351af0d731bSPaulXiCao }; 352af0d731bSPaulXiCao 353af0d731bSPaulXiCao int main() { 354af0d731bSPaulXiCao types::for_each(types::floating_point_types(), TestFloat()); 355af0d731bSPaulXiCao types::for_each(types::type_list<short, int, long, long long>(), TestInt()); 356af0d731bSPaulXiCao } 357