• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //  Copyright (c) 2012 Robert Ramey
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 #include <iostream>
8 #include <limits>
9 #include <functional>
10 #include <array>
11 
12 #include <boost/core/demangle.hpp>
13 #include <boost/safe_numerics/checked_result.hpp>
14 #include <boost/safe_numerics/checked_result_operations.hpp>
15 #include <boost/safe_numerics/interval.hpp>
16 
17 template<typename T>
18 using fptr = T (*)(const T &, const T &);
19 template<typename T>
20 using fptr_interval = fptr<boost::safe_numerics::interval<T>>;
21 
22 template<typename T>
23 struct op {
24     const fptr<T> m_f;
25     const fptr_interval<T> m_finterval;
26     const char * m_symbol;
27     const bool skip_zeros;
28 };
29 
30 template<
31     typename T,
32     unsigned int N
33 >
test_type_operator(const T (& value)[N],const op<T> & opi)34 bool test_type_operator(
35     const T (&value)[N],
36     const op<T> & opi
37 ){
38     using namespace boost::safe_numerics;
39 
40     // for each pair of values p1, p2 (100)
41     for(const T & l1 : value)
42     for(const T & u1 : value){
43         if(l1 > u1) continue; // skip reverse range
44         const interval<T> p1(l1, u1);
45         for(const T & l2 : value)
46         for(const T & u2 : value){
47             if(l2 > u2) continue; // skip reverse range
48             const interval<T> p2(l2, u2);
49 
50             // maybe skip intervals which include zero
51             if(opi.skip_zeros){
52                 if(l2 == safe_numerics_error::range_error
53                 || l2 == safe_numerics_error::domain_error
54                 || u2 == safe_numerics_error::range_error
55                 || u2 == safe_numerics_error::domain_error
56                 || p2.includes(T(0))
57                 )
58                     continue;
59             }
60 
61             // create a new interval from the operation
62             const interval<T> result_interval = opi.m_finterval(p1, p2);
63             std::cout
64                 << p1 << opi.m_symbol << p2 << " -> " << result_interval << std::endl;
65 
66             // if resulting interval is null
67             if(result_interval.u < result_interval.l)
68                 continue;
69 
70             // for each pair test values
71             for(const T r1 : value)
72             for(const T r2 : value){
73                 // calculate result of operation
74                 const T result = opi.m_f(r1, r2);
75                 if(result != safe_numerics_error::range_error
76                 && result != safe_numerics_error::domain_error ){
77                     // note usage of tribool logic here !!!
78                     // includes returns indeterminate the conditional
79                     // returns false in both cases and this is what we want.
80                     // This is very subtle, don't skim over this.
81                     // if both r1 and r2 are within they're respective bounds
82                     if(p1.includes(r1) && p2.includes(r2)
83                     && ! result_interval.includes(result)){
84                         #if 0
85                         const boost::logic::tribool b1 = p1.includes(r1);
86                         const boost::logic::tribool b2 = p2.includes(r2);
87                         const boost::logic::tribool b3 = result_interval.includes(result);
88                         const interval<T> result_intervalx = opi.m_finterval(p1, p2);
89                         const T resultx = opi.m_f(r1, r2);
90                         #endif
91                         return false;
92                     }
93                 }
94             }
95         }
96     }
97     return true;
98 }
99 
100 // values
101 // note: need to explicitly specify number of elements to avoid msvc failure
102 template<typename T>
103 const boost::safe_numerics::checked_result<T> value[8] = {
104     boost::safe_numerics::safe_numerics_error::negative_overflow_error,
105     std::numeric_limits<T>::lowest(),
106     T(-1),
107     T(0),
108     T(1),
109     std::numeric_limits<T>::max(),
110     boost::safe_numerics::safe_numerics_error::positive_overflow_error,
111     boost::safe_numerics::safe_numerics_error::domain_error
112 };
113 
114 // note: need to explicitly specify number of elements to avoid msvc failure
115 template<typename T>
116 const boost::safe_numerics::checked_result<T> unsigned_value[6] = {
117     boost::safe_numerics::safe_numerics_error::negative_overflow_error,
118     T(0),
119     T(1),
120     std::numeric_limits<T>::max(),
121     boost::safe_numerics::safe_numerics_error::positive_overflow_error,
122     boost::safe_numerics::safe_numerics_error::domain_error
123 };
124 
125 // invoke for each type
126 struct test_type {
127     unsigned int m_error_count;
test_typetest_type128     test_type() :
129         m_error_count(0)
130     {}
131     template<typename T>
operator ()test_type132     bool operator()(const T &){
133         using namespace boost::safe_numerics;
134         std::cout
135             << "** testing "
136             << boost::core::demangle(typeid(T).name())
137             << std::endl;
138 
139         using R = checked_result<T>;
140         // pointers to operands for types T
141         static const std::array<op<R>, 5> op_table{{
142             {operator+, operator+, "+", false},
143             {operator-, operator-, "-", false},
144             {operator*, operator*, "*", false},
145             {operator<<, operator<<, "<<", false},
146             {operator>>, operator>>, ">>", false},
147         }};
148 
149         //for(unsigned int i = 0; i < sizeof(op_table)/sizeof(op) / sizeof(fptr<R>); ++i){
150         for(const op<R> & o : op_table){
151             if(std::is_signed<T>::value){
152                 if(! test_type_operator(value<T>, o)){
153                     ++m_error_count;
154                     return false;
155                 }
156             }
157             else{
158                 if(! test_type_operator(unsigned_value<T>, o)){
159                     ++m_error_count;
160                     return false;
161                 }
162             }
163         }
164         return true;
165     }
166 };
167 
168 #include <boost/mp11/list.hpp>
169 #include <boost/mp11/algorithm.hpp>
170 
main(int,char * [])171 int main(int, char *[]){
172     using namespace boost::mp11;
173     // list of signed types
174     using signed_types = mp_list<std::int8_t, std::int16_t, std::int32_t, std::int64_t>;
175     // list of unsigned types
176     using unsigned_types = mp_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>;
177 
178     test_type t;
179     mp_for_each<unsigned_types>(t);
180     mp_for_each<signed_types>(t);
181 
182     std::cout << (t.m_error_count == 0 ? "success!" : "failure") << std::endl;
183     return t.m_error_count ;
184 }
185