1 /*################################################################################
2   ##
3   ##   Copyright (C) 2016-2020 Keith O'Hara
4   ##
5   ##   This file is part of the GCE-Math C++ library.
6   ##
7   ##   Licensed under the Apache License, Version 2.0 (the "License");
8   ##   you may not use this file except in compliance with the License.
9   ##   You may obtain a copy of the License at
10   ##
11   ##       http://www.apache.org/licenses/LICENSE-2.0
12   ##
13   ##   Unless required by applicable law or agreed to in writing, software
14   ##   distributed under the License is distributed on an "AS IS" BASIS,
15   ##   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   ##   See the License for the specific language governing permissions and
17   ##   limitations under the License.
18   ##
19   ################################################################################*/
20 
21 #include <cmath>
22 #include <ios>
23 #include <iostream>
24 #include <iomanip>
25 #include <string>
26 #include <functional>
27 
28 #include "gcem.hpp"
29 
30 //
31 // tolerance
32 
33 int GCEM_TEST_NUMBER = 0;
34 
35 #ifndef TEST_VAL_TYPES
36     #define TEST_VAL_TYPES long double
37     #define TEST_VAL_TYPES_V 1
38 #endif
39 
40 #ifndef TEST_ERR_TOL
41 #ifdef _WIN32
42     #define TEST_ERR_TOL 1e-10
43 #else
44     #define TEST_ERR_TOL 1e-14
45 #endif
46 #endif
47 
48 #ifdef TEST_VAL_TYPES_V
49 #define PRINT_ERR(err_val)                  \
50 {                                           \
51     printf(" error value = %LE.", err_val); \
52 }
53 #else
54 #define PRINT_ERR(err_val)                  \
55 {                                           \
56     printf(" error value = %d.", err_val);  \
57 }
58 #endif
59 
60 #ifdef TEST_VAL_TYPES_V
61     #define VAL_IS_INF(val) std::isinf(static_cast<long double>(val))
62     #define VAL_IS_NAN(val) std::isnan(static_cast<long double>(val))
63 #else
64     #define VAL_IS_INF(val) false
65     #define VAL_IS_NAN(val) false
66 #endif
67 
68 #ifndef TEST_NAN
69     #define TEST_NAN gcem::GCLIM<double>::quiet_NaN()
70 #endif
71 
72 #ifndef TEST_POSINF
73     #define TEST_POSINF gcem::GCLIM<double>::infinity()
74 #endif
75 
76 #ifndef TEST_NEGINF
77     #define TEST_NEGINF -gcem::GCLIM<double>::infinity()
78 #endif
79 
80 //
81 
82 #ifndef TEST_PRINT_LEVEL
83     #define TEST_PRINT_LEVEL 3
84 #endif
85 
86 #ifndef TEST_PRINT_PRECISION_1
87     #define TEST_PRINT_PRECISION_1 2
88 #endif
89 
90 #ifndef TEST_PRINT_PRECISION_2
91     #define TEST_PRINT_PRECISION_2 5
92 #endif
93 
94 #ifndef GCEM_UNUSED_PAR
95     #define GCEM_UNUSED_PAR(x) (void)(x)
96 #endif
97 
98 //
99 // printing pass
100 
101 #ifndef TRAVIS_CLANG_CXX
102 
103 #define TEST_PASS_PRINT_FINISH                                                                      \
104     if (print_level > 1 && !VAL_IS_NAN(err_val)) {                                                  \
105         std::cout << std::setprecision(3) << std::scientific                                        \
106                   << "    - error value = " << err_val << "\n";                                     \
107     }                                                                                               \
108                                                                                                     \
109     std::cout << std::defaultfloat;                                                                 \
110     std::cout << std::endl;                                                                         \
111 
112 #else
113 
114 #define TEST_PASS_PRINT_FINISH                                                                      \
115     if (print_level > 1 && !VAL_IS_NAN(err_val)) {                                                  \
116         std::cout << "    - error value = " << err_val << "\n";                                     \
117     }                                                                                               \
118                                                                                                     \
119     std::cout << std::endl;                                                                         \
120 
121 #endif
122 
123 //
124 
125 template<typename T1, typename T2, typename T3>
126 inline
127 void
print_test_pass(std::string fn_name,const int print_level,int print_precision_1,int print_precision_2,const T1 f_val,const T2 err_val,const T3 par_1)128 print_test_pass(std::string fn_name, const int print_level,
129                 int print_precision_1, int print_precision_2,
130                 const T1 f_val, const T2 err_val,
131                 const T3 par_1)
132 {
133     std::cout << "[\033[32mOK\033[0m] ";
134     std::cout << std::setiosflags(std::ios::fixed)
135               << std::setprecision(print_precision_1) << fn_name
136               << "(" << par_1 << ") = "
137               << std::setprecision(print_precision_2) << f_val << "\n";
138 
139     TEST_PASS_PRINT_FINISH
140 }
141 
142 template<typename T1, typename T2, typename T3, typename T4>
143 inline
144 void
print_test_pass(std::string fn_name,const int print_level,int print_precision_1,int print_precision_2,const T1 f_val,const T2 err_val,const T3 par_1,const T4 par_2)145 print_test_pass(std::string fn_name, const int print_level,
146                 int print_precision_1, int print_precision_2,
147                 const T1 f_val, const T2 err_val,
148                 const T3 par_1, const T4 par_2)
149 {
150     std::cout << "[\033[32mOK\033[0m] ";
151     std::cout << std::setiosflags(std::ios::fixed)
152               << std::setprecision(print_precision_1) << fn_name
153               << "(" << par_1 << "," << par_2 << ") = "
154               << std::setprecision(print_precision_2) << f_val << "\n";
155 
156     TEST_PASS_PRINT_FINISH
157 }
158 
159 template<typename T1, typename T2, typename T3, typename T4, typename T5>
160 inline
161 void
print_test_pass(std::string fn_name,const int print_level,int print_precision_1,int print_precision_2,const T1 f_val,const T2 err_val,const T3 par_1,const T4 par_2,const T5 par_3)162 print_test_pass(std::string fn_name, const int print_level,
163                 int print_precision_1, int print_precision_2,
164                 const T1 f_val, const T2 err_val,
165                 const T3 par_1, const T4 par_2, const T5 par_3)
166 {
167     std::cout << "[\033[32mOK\033[0m] ";
168     std::cout << std::setiosflags(std::ios::fixed)
169               << std::setprecision(print_precision_1) << fn_name
170               << "(" << par_1 << "," << par_2 << "," << par_3 << ") = "
171               << std::setprecision(print_precision_2) << f_val << "\n";
172 
173     TEST_PASS_PRINT_FINISH
174 }
175 
176 //
177 // printing fail
178 
179 template<typename T1, typename T2, typename T3>
180 inline
181 void
print_test_fail(std::string fn_name,int test_number,const int print_level,int print_precision_1,int print_precision_2,const T1 f_val,const T2 expected_val,const T3 par_1)182 print_test_fail(std::string fn_name, int test_number, const int print_level,
183                 int print_precision_1, int print_precision_2,
184                 const T1 f_val, const T2 expected_val,
185                 const T3 par_1)
186 {
187     GCEM_UNUSED_PAR(print_level);
188     GCEM_UNUSED_PAR(print_precision_1);
189     GCEM_UNUSED_PAR(print_precision_2);
190 
191     std::cerr << "\033[31m Test failed!\033[0m\n";
192     std::cerr << "  - Test number: " << test_number << "\n";
193     std::cerr << "  - Function Call:  " << fn_name
194               << "(" << par_1 << ");\n";
195     std::cerr << "  - Expected value: " << expected_val << "\n";
196     std::cerr << "  - Actual value:   " << f_val << "\n";
197     std::cerr << std::endl;
198 
199     throw std::runtime_error("test fail");
200 }
201 
202 template<typename T1, typename T2, typename T3, typename T4>
203 inline
204 void
print_test_fail(std::string fn_name,int test_number,const int print_level,int print_precision_1,int print_precision_2,const T1 f_val,const T2 expected_val,const T3 par_1,const T4 par_2)205 print_test_fail(std::string fn_name, int test_number, const int print_level,
206                 int print_precision_1, int print_precision_2,
207                 const T1 f_val, const T2 expected_val,
208                 const T3 par_1, const T4 par_2)
209 {
210     GCEM_UNUSED_PAR(print_level);
211     GCEM_UNUSED_PAR(print_precision_1);
212     GCEM_UNUSED_PAR(print_precision_2);
213 
214     std::cerr << "\033[31m Test failed!\033[0m\n";
215     std::cerr << "  - Test number: " << test_number << "\n";
216     std::cerr << "  - Function Call:  " << fn_name
217               << "(" << par_1 << "," << par_2 << ");\n";
218     std::cerr << "  - Expected value: " << expected_val << "\n";
219     std::cerr << "  - Actual value:   " << f_val << "\n";
220     std::cerr << std::endl;
221 
222     throw std::runtime_error("test fail");
223 }
224 
225 template<typename T1, typename T2, typename T3, typename T4, typename T5>
226 inline
227 void
print_test_fail(std::string fn_name,int test_number,const int print_level,int print_precision_1,int print_precision_2,const T1 f_val,const T2 expected_val,const T3 par_1,const T4 par_2,const T5 par_3)228 print_test_fail(std::string fn_name, int test_number, const int print_level,
229                 int print_precision_1, int print_precision_2,
230                 const T1 f_val, const T2 expected_val,
231                 const T3 par_1, const T4 par_2, const T5 par_3)
232 {
233     GCEM_UNUSED_PAR(print_level);
234     GCEM_UNUSED_PAR(print_precision_1);
235     GCEM_UNUSED_PAR(print_precision_2);
236 
237     std::cerr << "\033[31m Test failed!\033[0m\n";
238     std::cerr << "  - Test number: " << test_number << "\n";
239     std::cerr << "  - Function Call:  " << fn_name
240               << "(" << par_1 << "," << par_2 << "," << par_3 << ");\n";
241     std::cerr << "  - Expected value: " << expected_val << "\n";
242     std::cerr << "  - Actual value:   " << f_val << "\n";
243     std::cerr << std::endl;
244 
245     throw std::runtime_error("test fail");
246 }
247 
248 //
249 // macros
250 
251 #define GCEM_TEST_EXPECTED_VAL(gcem_fn, expected_val, ...)                                          \
252 {                                                                                                   \
253     ++GCEM_TEST_NUMBER;                                                                             \
254     std::string fn_name = #gcem_fn;                                                                 \
255                                                                                                     \
256     auto f_val = gcem_fn(__VA_ARGS__);                                                              \
257                                                                                                     \
258     auto err_val = std::abs(f_val - expected_val) / (1 + std::abs(expected_val));                   \
259                                                                                                     \
260     bool test_success = false;                                                                      \
261                                                                                                     \
262     if (VAL_IS_NAN(expected_val) && VAL_IS_NAN(f_val)) {                                            \
263         test_success = true;                                                                        \
264     } else if(!VAL_IS_NAN(f_val) && VAL_IS_INF(f_val) && f_val == expected_val) {                   \
265         test_success = true;                                                                        \
266     } else if(err_val < TEST_ERR_TOL) {                                                             \
267         test_success = true;                                                                        \
268     } else {                                                                                        \
269         print_test_fail(fn_name,GCEM_TEST_NUMBER,TEST_PRINT_LEVEL,                                  \
270                         TEST_PRINT_PRECISION_1,TEST_PRINT_PRECISION_2,                              \
271                         f_val,expected_val,__VA_ARGS__);                                            \
272     }                                                                                               \
273                                                                                                     \
274     if (test_success && TEST_PRINT_LEVEL > 0)                                                       \
275     {                                                                                               \
276         print_test_pass(fn_name,TEST_PRINT_LEVEL,                                                   \
277                         TEST_PRINT_PRECISION_1,TEST_PRINT_PRECISION_2,                              \
278                         f_val,err_val,__VA_ARGS__);                                                 \
279     }                                                                                               \
280 }
281 
282 #define GCEM_TEST_COMPARE_VALS(gcem_fn, std_fn, ...)                                                \
283 {                                                                                                   \
284     ++GCEM_TEST_NUMBER;                                                                             \
285                                                                                                     \
286     std::string fn_name = #gcem_fn;                                                                 \
287                                                                                                     \
288     auto gcem_fn_val = gcem_fn(__VA_ARGS__);                                                        \
289     auto std_fn_val  = std_fn(__VA_ARGS__);                                                         \
290                                                                                                     \
291     auto err_val = std::abs(gcem_fn_val - std_fn_val) / (1 + std::abs(std_fn_val));                 \
292                                                                                                     \
293     bool test_success = false;                                                                      \
294                                                                                                     \
295     if (VAL_IS_NAN(gcem_fn_val) && VAL_IS_NAN(std_fn_val)) {                                        \
296         test_success = true;                                                                        \
297     } else if(!VAL_IS_NAN(gcem_fn_val) && VAL_IS_INF(gcem_fn_val) && gcem_fn_val == std_fn_val) {   \
298         test_success = true;                                                                        \
299     } else if(err_val < TEST_ERR_TOL) {                                                             \
300         test_success = true;                                                                        \
301     } else {                                                                                        \
302         print_test_fail(fn_name,GCEM_TEST_NUMBER,TEST_PRINT_LEVEL,                                  \
303                         TEST_PRINT_PRECISION_1,TEST_PRINT_PRECISION_2,                              \
304                         gcem_fn_val,std_fn_val,__VA_ARGS__);                                        \
305     }                                                                                               \
306                                                                                                     \
307     if (test_success && TEST_PRINT_LEVEL > 0)                                                       \
308     {                                                                                               \
309         print_test_pass(fn_name,TEST_PRINT_LEVEL,                                                   \
310                         TEST_PRINT_PRECISION_1,TEST_PRINT_PRECISION_2,                              \
311                         gcem_fn_val,err_val,__VA_ARGS__);                                           \
312     }                                                                                               \
313 }
314 
315 //
316 // begin and end print
317 
318 inline
319 void
finish_print_line(int k)320 finish_print_line(int k)
321 {
322     for (int i=0; i < 80 - k; ++i) {
323         printf(" ");
324     }
325     printf("#");
326 }
327 
328 inline
329 void
print_begin(std::string fn_name)330 print_begin(std::string fn_name)
331 {
332     // GCEM_UNUSED_PAR(fn_name);
333     if (TEST_PRINT_LEVEL > 0) {
334     std::cout <<
335         "\n################################################################################\n";
336     std::cout <<
337         "\n\033[36m~~~~ Begin tests for: " << fn_name << "\033[0m\n";
338     std::cout << std::endl;
339     }
340 }
341 
342 inline
343 void
print_final(std::string fn_name)344 print_final(std::string fn_name)
345 {
346     std::cout.precision(1);
347 
348     std::cout <<
349         "################################################################################\n" <<
350         "#                                TESTS SUMMARY                                 #\n";
351 
352     //
353 
354     std::cout <<
355         "#  - Test suite: " << fn_name;
356         finish_print_line(14 + int(fn_name.length()) + 4);
357     std::cout << std::endl;
358 
359     //
360 
361     std::cout <<
362         "#  - Number of tests: " << GCEM_TEST_NUMBER;
363         finish_print_line(20 + (GCEM_TEST_NUMBER > 9 ? 1 : 0) + 4);
364     std::cout << std::endl;
365 
366     //
367 
368     std::cout <<
369         "#  - Error tolerance: " << std::scientific << TEST_ERR_TOL;
370         finish_print_line(30);
371     std::cout << std::endl;
372 
373     //
374 
375     std::cout <<
376         "#                                   [\033[32mPASS\033[0m]                                     #\n" <<
377         "################################################################################\n";
378     //
379 
380     std::cout << std::endl;
381 }
382