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