1 // integer_test_suite.hpp : arithmetic test suite for abitrary precision integers
2 //
3 // Copyright (C) 2017-2021 Stillwater Supercomputing, Inc.
4 //
5 // This file is part of the universal numbers project, which is released under an MIT Open Source license.
6 #include <iostream>
7 #include <string>
8 
9 // the integer number class will be configured outside of this helper
10 //
11 /*
12    The goal of the arbitrary integers is to provide a constrained big integer type
13    that enables fast computation with exceptions for overflow, so that the type
14    can be used for forward error analysis studies.
15 */
16 #include <universal/verification/test_status.hpp> // ReportTestResult used by test suite runner
17 #include <universal/verification/test_reporters.hpp>
18 
19 namespace sw::universal {
20 
21 	// enumerate all addition cases for an integer<16> configuration compared against native short
22 	template<typename BlockType, size_t testBits = 12>
VerifyShortAddition(bool bReportIndividualTestCases)23 	int VerifyShortAddition(bool bReportIndividualTestCases) {
24 		constexpr size_t nbits = 16;
25 		constexpr size_t NR_INTEGERS = (size_t(1) << testBits);
26 
27 		using Integer = integer<nbits, BlockType>;
28 		Integer ia, ib, iresult, iref;
29 
30 		int nrOfFailedTests = 0;
31 		for (size_t i = 0; i < NR_INTEGERS; i++) {
32 			ia.setbits(i);
33 			short i64a = short(ia);
34 			for (size_t j = 0; j < NR_INTEGERS; j++) {
35 				ib.setbits(j);
36 				short i64b = short(ib);
37 				iref = i64a + i64b;
38 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
39 				try {
40 					iresult = ia + ib;
41 				}
42 				catch (...) {
43 					Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg);
44 					if (iref > max_int || iref < min_int) {
45 						// correctly caught the exception
46 						continue;
47 					}
48 					else {
49 						nrOfFailedTests++;
50 					}
51 				}
52 
53 #else
54 				iresult = ia + ib;
55 #endif
56 				if (iresult != iref) {
57 					nrOfFailedTests++;
58 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "+", ia, ib, iref, iresult);
59 				}
60 				else {
61 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "+", ia, ib, iref, iresult);
62 				}
63 			}
64 			if (i % 1024 == 0) std::cout << '.';
65 		}
66 		std::cout << std::endl;
67 
68 		return nrOfFailedTests;
69 	}
70 	// enumerate all subtraction cases for an integer<16> configuration compared against native short
71 	template<typename BlockType, size_t testBits = 12>
VerifyShortSubtraction(bool bReportIndividualTestCases)72 	int VerifyShortSubtraction(bool bReportIndividualTestCases) {
73 		constexpr size_t nbits = 16;
74 		constexpr size_t NR_INTEGERS = (size_t(1) << testBits);
75 
76 		using Integer = integer<nbits, BlockType>;
77 		Integer ia, ib, iresult, iref;
78 
79 		int nrOfFailedTests = 0;
80 		for (size_t i = 0; i < NR_INTEGERS; i++) {
81 			ia.setbits(i);
82 			short i16a = short(ia);
83 			for (size_t j = 0; j < NR_INTEGERS; j++) {
84 				ib.setbits(j);
85 				short i16b = short(ib);
86 				iref = i16a - i16b;
87 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
88 				try {
89 					iresult = ia - ib;
90 				}
91 				catch (...) {
92 					Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg);
93 					if (iref > max_int || iref < min_int) {
94 						// correctly caught the exception
95 						continue;
96 					}
97 					else {
98 						nrOfFailedTests++;
99 					}
100 				}
101 
102 #else
103 				iresult = ia - ib;
104 #endif
105 				if (iresult != iref) {
106 					nrOfFailedTests++;
107 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "-", ia, ib, iref, iresult);
108 				}
109 				else {
110 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "-", ia, ib, iref, iresult);
111 				}
112 			}
113 			if (i % 1024 == 0) std::cout << '.';
114 		}
115 		std::cout << std::endl;
116 
117 		return nrOfFailedTests;
118 	}
119 	// enumerate all multiplication cases for an integer<16> configuration compared against native short
120 	template<typename BlockType, size_t testBits = 12>
VerifyShortMultiplication(bool bReportIndividualTestCases)121 	int VerifyShortMultiplication(bool bReportIndividualTestCases) {
122 		constexpr size_t nbits = 16;
123 		constexpr size_t NR_INTEGERS = (size_t(1) << testBits);
124 
125 		using Integer = integer<nbits, BlockType>;
126 		Integer ia, ib, iresult, iref;
127 
128 		int nrOfFailedTests = 0;
129 		for (size_t i = 0; i < NR_INTEGERS; i++) {
130 			ia.setbits(i);
131 			short i16a = short(ia);
132 			for (size_t j = 0; j < NR_INTEGERS; j++) {
133 				ib.setbits(j);
134 				short i16b = short(ib);
135 				iref = i16a * i16b;
136 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
137 				try {
138 					iresult = ia * ib;
139 				}
140 				catch (...) {
141 					Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg);
142 					if (iref > max_int || iref < min_int) {
143 						// correctly caught the exception
144 						continue;
145 					}
146 					else {
147 						nrOfFailedTests++;
148 					}
149 				}
150 
151 #else
152 				iresult = ia * ib;
153 #endif
154 				if (iresult != iref) {
155 					nrOfFailedTests++;
156 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "*", ia, ib, iref, iresult);
157 				}
158 				else {
159 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "*", ia, ib, iref, iresult);
160 				}
161 			}
162 			if (i % 1024 == 0) std::cout << '.';
163 		}
164 		std::cout << std::endl;
165 
166 		return nrOfFailedTests;
167 	}
168 	// enumerate all division cases for an integer<16> configuration compared against native short
169 	template<typename BlockType, size_t testBits = 10>
VerifyShortDivision(bool bReportIndividualTestCases)170 	int VerifyShortDivision(bool bReportIndividualTestCases) {
171 		constexpr size_t nbits = 16;
172 		constexpr size_t NR_INTEGERS = (size_t(1) << testBits);
173 
174 		using Integer = integer<nbits, BlockType>;
175 		Integer ia, ib, iresult, iref;
176 
177 		int nrOfFailedTests = 0;
178 		for (size_t i = 0; i < NR_INTEGERS; i++) {
179 			ia.setbits(i);
180 			short i16a = short(ia);
181 			for (size_t j = 0; j < NR_INTEGERS; j++) {
182 				ib.setbits(j);
183 				short i16b = short(ib);
184 				if (j > 0) iref = i16a / i16b;
185 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
186 				if (j == 0) {
187 					try {
188 						iresult = ia / ib;
189 					}
190 					catch (const integer_divide_by_zero& err) {
191 						// correctly caught overflow
192 						continue;
193 					}
194 					catch (...) {
195 						nrOfFailedTests++;
196 					}
197 				}
198 				Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg);
199 				if (iref > max_int || iref < min_int) {
200 					try {
201 						iresult = ia / ib;
202 					}
203 					catch (const integer_overflow& err) {
204 						// correctly caught overflow
205 						continue;
206 					}
207 					catch (...) {
208 						nrOfFailedTests++;
209 					}
210 				}
211 #else
212 				if (j == 0) continue;
213 				iresult = ia / ib;
214 #endif
215 
216 				if (iresult != iref) {
217 					nrOfFailedTests++;
218 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "/", ia, ib, iref, iresult);
219 				}
220 				else {
221 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "/", ia, ib, iref, iresult);
222 				}
223 			}
224 			if (i % 1024 == 0) std::cout << '.';
225 		}
226 		std::cout << std::endl;
227 
228 		return nrOfFailedTests;
229 	}
230 	// enumerate all remainder cases for an integer<16> configuration compared against native short
231 	template<typename BlockType, size_t testBits = 10>
VerifyShortRemainder(bool bReportIndividualTestCases)232 	int VerifyShortRemainder(bool bReportIndividualTestCases) {
233 		constexpr size_t nbits = 16;
234 		constexpr size_t NR_INTEGERS = (size_t(1) << testBits);
235 
236 		using Integer = integer<nbits, BlockType>;
237 		Integer ia, ib, iresult, iref;
238 
239 		int nrOfFailedTests = 0;
240 		for (size_t i = 0; i < NR_INTEGERS; i++) {
241 			ia.setbits(i);
242 			short i16a = short(ia);
243 			for (size_t j = 0; j < NR_INTEGERS; j++) {
244 				ib.setbits(j);
245 				short i16b = short(ib);
246 				if (j > 0) iref = i16a % i16b;
247 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
248 				try {
249 					iresult = ia % ib;
250 				}
251 				catch (...) {
252 					Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg);
253 					if (iref > max_int || iref < min_int) {
254 						// correctly caught the exception
255 						continue;
256 					}
257 					else {
258 						nrOfFailedTests++;
259 					}
260 				}
261 #else
262 				iresult = ia % ib;
263 #endif
264 				if (iresult != iref) {
265 					nrOfFailedTests++;
266 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "%", ia, ib, iref, iresult);
267 				}
268 				else {
269 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "%", ia, ib, iref, iresult);
270 				}
271 			}
272 			if (i % 1024 == 0) std::cout << '.';
273 		}
274 		std::cout << std::endl;
275 
276 		return nrOfFailedTests;
277 	}
278 
279 	// enumerate all addition cases for an integer<nbits, BlockType> configuration
280 	template<size_t nbits, typename BlockType>
VerifyAddition(bool bReportIndividualTestCases)281 	int VerifyAddition(bool bReportIndividualTestCases) {
282 		using Integer = integer<nbits, BlockType>;
283 		constexpr size_t NR_INTEGERS = (size_t(1) << nbits);
284 
285 		Integer ia, ib, iresult, iref;
286 
287 		int nrOfFailedTests = 0;
288 		for (size_t i = 0; i < NR_INTEGERS; i++) {
289 			ia.setbits(i);
290 			int64_t i64a = int64_t(ia);
291 			for (size_t j = 0; j < NR_INTEGERS; j++) {
292 				ib.setbits(j);
293 				int64_t i64b = int64_t(ib);
294 				iref = i64a + i64b;
295 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
296 				try {
297 					iresult = ia + ib;
298 				}
299 				catch (...) {
300 					Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg);
301 					if (iref > max_int || iref < min_int) {
302 						// correctly caught the exception
303 
304 					}
305 					else {
306 						nrOfFailedTests++;
307 					}
308 				}
309 
310 #else
311 				iresult = ia + ib;
312 #endif
313 				if (iresult != iref) {
314 					nrOfFailedTests++;
315 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "+", ia, ib, iref, iresult);
316 				}
317 				else {
318 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "+", ia, ib, iref, iresult);
319 				}
320 				if (nrOfFailedTests > 100) return nrOfFailedTests;
321 			}
322 			if (i % 1024 == 0) std::cout << '.';
323 		}
324 		std::cout << std::endl;
325 		return nrOfFailedTests;
326 	}
327 	// enumerate all subtraction cases for an integer<nbits, BlockType> configuration
328 	template<size_t nbits, typename BlockType>
VerifySubtraction(bool bReportIndividualTestCases)329 	int VerifySubtraction(bool bReportIndividualTestCases) {
330 		using Integer = integer<nbits, BlockType>;
331 		constexpr size_t NR_INTEGERS = (size_t(1) << nbits);
332 
333 		Integer ia, ib, iresult, iref;
334 
335 		int nrOfFailedTests = 0;
336 		for (size_t i = 0; i < NR_INTEGERS; i++) {
337 			ia.setbits(i);
338 			int64_t i64a = int64_t(ia);
339 			for (size_t j = 0; j < NR_INTEGERS; j++) {
340 				ib.setbits(j);
341 				int64_t i64b = int64_t(ib);
342 				iref = i64a - i64b;
343 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
344 				try {
345 					iresult = ia - ib;
346 				}
347 				catch (...) {
348 					Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg);
349 					if (iref > max_int || iref < min_int) {
350 						// correctly caught the exception
351 
352 					}
353 					else {
354 						nrOfFailedTests++;
355 					}
356 				}
357 
358 #else
359 				iresult = ia - ib;
360 #endif
361 				if (iresult != iref) {
362 					nrOfFailedTests++;
363 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "-", ia, ib, iref, iresult);
364 				}
365 				else {
366 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "-", ia, ib, iref, iresult);
367 				}
368 				if (nrOfFailedTests > 100) return nrOfFailedTests;
369 			}
370 			if (i % 1024 == 0) std::cout << '.';
371 		}
372 		std::cout << std::endl;
373 		return nrOfFailedTests;
374 	}
375 
376 	// enumerate all multiplication cases for an integer<nbits, BlockType> configuration
377 	template<size_t nbits, typename BlockType>
VerifyMultiplication(bool bReportIndividualTestCases)378 	int VerifyMultiplication(bool bReportIndividualTestCases) {
379 		using Integer = integer<nbits, BlockType>;
380 		constexpr size_t NR_INTEGERS = (size_t(1) << nbits);
381 
382 		Integer ia, ib, iresult, iref;
383 
384 		int nrOfFailedTests = 0;
385 		for (size_t i = 0; i < NR_INTEGERS; i++) {
386 			ia.setbits(i);
387 			int64_t i64a = int64_t(ia);
388 			for (size_t j = 0; j < NR_INTEGERS; j++) {
389 				ib.setbits(j);
390 				int64_t i64b = int64_t(ib);
391 				iref = i64a * i64b;
392 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
393 				try {
394 					iresult = ia * ib;
395 				}
396 				catch (...) {
397 					Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg);
398 					if (iref > max_int || iref < min_int) {
399 						// correctly caught the exception
400 
401 					}
402 					else {
403 						nrOfFailedTests++;
404 					}
405 				}
406 
407 #else
408 				iresult = ia * ib;
409 #endif
410 				if (iresult != iref) {
411 					nrOfFailedTests++;
412 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "*", ia, ib, iref, iresult);
413 				}
414 				else {
415 					if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "*", ia, ib, iref, iresult);
416 				}
417 				if (nrOfFailedTests > 100) return nrOfFailedTests;
418 			}
419 			if (i % 1024 == 0) std::cout << '.';
420 		}
421 		std::cout << std::endl;
422 		return nrOfFailedTests;
423 	}
424 
425 	// enumerate all division cases for an integer<nbits, BlockType> configuration
426 	template<size_t nbits, typename BlockType>
VerifyDivision(bool bReportIndividualTestCases)427 	int VerifyDivision(bool bReportIndividualTestCases) {
428 		using Integer = integer<nbits, BlockType>;
429 		constexpr size_t NR_INTEGERS = (size_t(1) << nbits);
430 
431 		Integer ia, ib, iresult, iref;
432 
433 		int nrOfFailedTests = 0;
434 		for (size_t i = 0; i < NR_INTEGERS; i++) {
435 			ia.setbits(i);
436 			int64_t i64a = int64_t(ia);
437 			for (size_t j = 0; j < NR_INTEGERS; j++) {
438 				ib.setbits(j);
439 				int64_t i64b = int64_t(ib);
440 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
441 				try {
442 					iresult = ia / ib;
443 				}
444 				catch (const integer_divide_by_zero& e) {
445 					if (ib == integer<nbits, BlockType>(0)) {
446 						// correctly caught the exception
447 						continue;
448 					}
449 					else {
450 						std::cerr << "unexpected : " << e.what() << std::endl;
451 						nrOfFailedTests++;
452 					}
453 				}
454 				catch (const integer_overflow& e) {
455 					std::cerr << e.what() << std::endl;
456 					// TODO: how do you validate the overflow?
457 				}
458 				catch (...) {
459 					std::cerr << "unexpected exception" << std::endl;
460 					nrOfFailedTests++;
461 				}
462 #else
463 				iresult = ia / ib;
464 #endif
465 				if (j == 0) {
466 					iref = 0; // or maxneg?
467 				}
468 				else {
469 					iref = i64a / i64b;
470 				}
471 				if (iresult != iref) {
472 					nrOfFailedTests++;
473 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "/", ia, ib, iref, iresult);
474 				}
475 				else {
476 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "/", ia, ib, iref, iresult);
477 				}
478 				if (nrOfFailedTests > 100) return nrOfFailedTests;
479 			}
480 			if (i % 1024 == 0) std::cout << '.';
481 		}
482 		std::cout << std::endl;
483 		return nrOfFailedTests;
484 	}
485 
486 	// enumerate all remainder cases for an integer<nbits, BlockType> configuration
487 	template<size_t nbits, typename BlockType>
VerifyRemainder(bool bReportIndividualTestCases)488 	int VerifyRemainder(bool bReportIndividualTestCases) {
489 		using Integer = integer<nbits, BlockType>;
490 		constexpr size_t NR_INTEGERS = (size_t(1) << nbits);
491 
492 		Integer ia, ib, iresult, iref;
493 
494 		int nrOfFailedTests = 0;
495 		for (size_t i = 0; i < NR_INTEGERS; i++) {
496 			ia.setbits(i);
497 			int64_t i64a = int64_t(ia);
498 			for (size_t j = 0; j < NR_INTEGERS; j++) {
499 				ib.setbits(j);
500 				int64_t i64b = int64_t(ib);
501 #if INTEGER_THROW_ARITHMETIC_EXCEPTION
502 				try {
503 					iresult = ia % ib;
504 				}
505 				catch (...) {
506 					if (ib == integer<nbits, BlockType>(0)) {
507 						// correctly caught the exception
508 						continue;
509 					}
510 					else {
511 						nrOfFailedTests++;
512 					}
513 				}
514 
515 #else
516 				iresult = ia % ib;
517 #endif
518 				iref = i64a % i64b;
519 				if (iresult != iref) {
520 					nrOfFailedTests++;
521 					if (bReportIndividualTestCases)	ReportBinaryArithmeticError("FAIL", "%", ia, ib, iref, iresult);
522 				}
523 				else {
524 					//if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "%", ia, ib, iref, iresult);
525 				}
526 				if (nrOfFailedTests > 100) return nrOfFailedTests;
527 			}
528 			if (i % 1024 == 0) std::cout << '.';
529 		}
530 		std::cout << std::endl;
531 		return nrOfFailedTests;
532 	}
533 
534 } // namespace sw::universal
535