1 //  (C) Copyright John Maddock 2006.
2 //  Use, modification and distribution are subject to the
3 //  Boost Software License, Version 1.0. (See accompanying file
4 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #include <boost/math/special_functions/gamma.hpp>
7 #include <boost/math/special_functions/erf.hpp> // for inverses
8 #include <boost/math/constants/constants.hpp>
9 #include <fstream>
10 #include <boost/math/tools/test_data.hpp>
11 #include "mp_t.hpp"
12 
13 using namespace boost::math::tools;
14 using namespace std;
15 
16 float external_f;
force_truncate(const float * f)17 float force_truncate(const float* f)
18 {
19    external_f = *f;
20    return external_f;
21 }
22 
truncate_to_float(mp_t r)23 float truncate_to_float(mp_t r)
24 {
25    float f = boost::math::tools::real_cast<float>(r);
26    return force_truncate(&f);
27 }
28 
29 struct erf_data_generator
30 {
operator ()erf_data_generator31    boost::math::tuple<mp_t, mp_t> operator()(mp_t z)
32    {
33       // very naively calculate spots using the gamma function at high precision:
34       int sign = 1;
35       if(z < 0)
36       {
37          sign = -1;
38          z = -z;
39       }
40       mp_t g1, g2;
41       g1 = boost::math::tgamma_lower(mp_t(0.5), z * z);
42       g1 /= sqrt(boost::math::constants::pi<mp_t>());
43       g1 *= sign;
44 
45       if(z < 0.5)
46       {
47          g2 = 1 - (sign * g1);
48       }
49       else
50       {
51          g2 = boost::math::tgamma(mp_t(0.5), z * z);
52          g2 /= sqrt(boost::math::constants::pi<mp_t>());
53       }
54       if(sign < 1)
55          g2 = 2 - g2;
56       return boost::math::make_tuple(g1, g2);
57    }
58 };
59 
double_factorial(int N)60 double double_factorial(int N)
61 {
62    double result = 1;
63    while(N > 2)
64    {
65       N -= 2;
66       result *= N;
67    }
68    return result;
69 }
70 
asymptotic_limit(int Bits)71 void asymptotic_limit(int Bits)
72 {
73    //
74    // The following block of code estimates how large z has
75    // to be before we can use the asymptotic expansion for
76    // erf/erfc and still get convergence: the series becomes
77    // divergent eventually so we have to be careful!
78    //
79    double result = (std::numeric_limits<double>::max)();
80    int terms = 0;
81    for(int n = 1; n < 15; ++n)
82    {
83       double lim = (Bits-n) * log(2.0) - log(sqrt(3.14)) + log(double_factorial(2*n+1));
84       double x = 1;
85       while(x*x + (2*n+1)*log(x) <= lim)
86          x += 0.1;
87       if(x < result)
88       {
89          result = x;
90          terms = n;
91       }
92    }
93 
94    std::cout << "Erf asymptotic limit for "
95       << Bits << " bit numbers is "
96       << result << " after approximately "
97       << terms << " terms." << std::endl;
98 
99    result = (std::numeric_limits<double>::max)();
100    terms = 0;
101    for(int n = 1; n < 30; ++n)
102    {
103       double x = pow(double_factorial(2*n+1)/pow(2.0, n-Bits), 1 / (2.0*n));
104       if(x < result)
105       {
106          result = x;
107          terms = n;
108       }
109    }
110 
111    std::cout << "Erfc asymptotic limit for "
112       << Bits << " bit numbers is "
113       << result << " after approximately "
114       << terms << " terms." << std::endl;
115 }
116 
erfc_inv(mp_t r)117 boost::math::tuple<mp_t, mp_t> erfc_inv(mp_t r)
118 {
119    mp_t x = exp(-r * r);
120    x = x.convert_to<double>();
121    std::cout << x << "   ";
122    mp_t result = boost::math::erfc_inv(x);
123    std::cout << result << std::endl;
124    return boost::math::make_tuple(x, result);
125 }
126 
127 
main(int argc,char * argv[])128 int main(int argc, char*argv [])
129 {
130    parameter_info<mp_t> arg1;
131    test_data<mp_t> data;
132 
133    bool cont;
134    std::string line;
135 
136    if(argc >= 2)
137    {
138       if(strcmp(argv[1], "--limits") == 0)
139       {
140          asymptotic_limit(24);
141          asymptotic_limit(53);
142          asymptotic_limit(64);
143          asymptotic_limit(106);
144          asymptotic_limit(113);
145          return 0;
146       }
147       else if(strcmp(argv[1], "--erf_inv") == 0)
148       {
149          mp_t (*f)(mp_t);
150          f = boost::math::erf_inv;
151          std::cout << "Welcome.\n"
152             "This program will generate spot tests for the inverse erf function:\n";
153          std::cout << "Enter the number of data points: ";
154          int points;
155          std::cin >> points;
156          data.insert(f, make_random_param(mp_t(-1), mp_t(1), points));
157       }
158       else if(strcmp(argv[1], "--erfc_inv") == 0)
159       {
160          boost::math::tuple<mp_t, mp_t> (*f)(mp_t);
161          f = erfc_inv;
162          std::cout << "Welcome.\n"
163             "This program will generate spot tests for the inverse erfc function:\n";
164          std::cout << "Enter the maximum *result* expected from erfc_inv: ";
165          double max_val;
166          std::cin >> max_val;
167          std::cout << "Enter the number of data points: ";
168          int points;
169          std::cin >> points;
170          parameter_info<mp_t> arg = make_random_param(mp_t(0), mp_t(max_val), points);
171          arg.type |= dummy_param;
172          data.insert(f, arg);
173       }
174    }
175    else
176    {
177       std::cout << "Welcome.\n"
178          "This program will generate spot tests for the erf and erfc functions:\n"
179          "  erf(z) and erfc(z)\n\n";
180 
181       do{
182          if(0 == get_user_parameter_info(arg1, "a"))
183             return 1;
184          data.insert(erf_data_generator(), arg1);
185 
186          std::cout << "Any more data [y/n]?";
187          std::getline(std::cin, line);
188          boost::algorithm::trim(line);
189          cont = (line == "y");
190       }while(cont);
191    }
192 
193    std::cout << "Enter name of test data file [default=erf_data.ipp]";
194    std::getline(std::cin, line);
195    boost::algorithm::trim(line);
196    if(line == "")
197       line = "erf_data.ipp";
198    std::ofstream ofs(line.c_str());
199    ofs << std::scientific << std::setprecision(40);
200    write_code(ofs, data, "erf_data");
201 
202    return 0;
203 }
204 
205 /* Output for asymptotic limits:
206 
207 Erf asymptotic limit for 24 bit numbers is 2.8 after approximately 6 terms.
208 Erfc asymptotic limit for 24 bit numbers is 4.12064 after approximately 17 terms.
209 Erf asymptotic limit for 53 bit numbers is 4.3 after approximately 11 terms.
210 Erfc asymptotic limit for 53 bit numbers is 6.19035 after approximately 29 terms.
211 Erf asymptotic limit for 64 bit numbers is 4.8 after approximately 12 terms.
212 Erfc asymptotic limit for 64 bit numbers is 7.06004 after approximately 29 terms.
213 Erf asymptotic limit for 106 bit numbers is 6.5 after approximately 14 terms.
214 Erfc asymptotic limit for 106 bit numbers is 11.6626 after approximately 29 terms.
215 Erf asymptotic limit for 113 bit numbers is 6.8 after approximately 14 terms.
216 Erfc asymptotic limit for 113 bit numbers is 12.6802 after approximately 29 terms.
217 */
218 
219