[12] | 1 | /* |
---|
| 2 | * A test program for boost/rational.hpp. |
---|
| 3 | * Change the typedef at the beginning of run_tests() to try out different |
---|
| 4 | * integer types. (These tests are designed only for signed integer |
---|
| 5 | * types. They should work for short, int and long.) |
---|
| 6 | * |
---|
| 7 | * (C) Copyright Stephen Silver, 2001. Permission to copy, use, modify, sell |
---|
| 8 | * and distribute this software is granted provided this copyright notice |
---|
| 9 | * appears in all copies. This software is provided "as is" without express or |
---|
| 10 | * implied warranty, and with no claim as to its suitability for any purpose. |
---|
| 11 | * |
---|
| 12 | * Incorporated into the boost rational number library, and modified and |
---|
| 13 | * extended, by Paul Moore, with permission. |
---|
| 14 | */ |
---|
| 15 | |
---|
| 16 | // Revision History |
---|
| 17 | // 04 Mar 01 Patches for Intel C++ and GCC (David Abrahams) |
---|
| 18 | |
---|
| 19 | #include "boost/rational.hpp" |
---|
| 20 | #include "boost/operators.hpp" |
---|
| 21 | #include <cstdlib> |
---|
| 22 | #include <iomanip> |
---|
| 23 | #include <iostream> |
---|
| 24 | #include <cstring> |
---|
| 25 | |
---|
| 26 | #ifndef BOOST_NO_STRINGSTREAM |
---|
| 27 | # include <sstream> |
---|
| 28 | #else |
---|
| 29 | # include <strstream> |
---|
| 30 | namespace { |
---|
| 31 | class unfreezer { |
---|
| 32 | public: |
---|
| 33 | unfreezer(std::ostrstream& s) : m_stream(s) {} |
---|
| 34 | ~unfreezer() { m_stream.freeze(false); } |
---|
| 35 | private: |
---|
| 36 | std::ostrstream& m_stream; |
---|
| 37 | }; |
---|
| 38 | } |
---|
| 39 | #endif |
---|
| 40 | |
---|
| 41 | // We can override this on the compile, as -DINT_TYPE=short or whatever. |
---|
| 42 | // The default test is against rational<long>. |
---|
| 43 | #ifndef INT_TYPE |
---|
| 44 | #define INT_TYPE long |
---|
| 45 | #endif |
---|
| 46 | |
---|
| 47 | namespace { |
---|
| 48 | |
---|
| 49 | // This is a trivial user-defined wrapper around the built in int type. |
---|
| 50 | // It can be used as a test type for rational<> |
---|
| 51 | class MyInt : boost::operators<MyInt> |
---|
| 52 | { |
---|
| 53 | int val; |
---|
| 54 | public: |
---|
| 55 | MyInt(int n = 0) : val(n) {} |
---|
| 56 | friend MyInt operator+ (const MyInt&); |
---|
| 57 | friend MyInt operator- (const MyInt&); |
---|
| 58 | MyInt& operator+= (const MyInt& rhs) { val += rhs.val; return *this; } |
---|
| 59 | MyInt& operator-= (const MyInt& rhs) { val -= rhs.val; return *this; } |
---|
| 60 | MyInt& operator*= (const MyInt& rhs) { val *= rhs.val; return *this; } |
---|
| 61 | MyInt& operator/= (const MyInt& rhs) { val /= rhs.val; return *this; } |
---|
| 62 | MyInt& operator%= (const MyInt& rhs) { val %= rhs.val; return *this; } |
---|
| 63 | MyInt& operator|= (const MyInt& rhs) { val |= rhs.val; return *this; } |
---|
| 64 | MyInt& operator&= (const MyInt& rhs) { val &= rhs.val; return *this; } |
---|
| 65 | MyInt& operator^= (const MyInt& rhs) { val ^= rhs.val; return *this; } |
---|
| 66 | const MyInt& operator++() { ++val; return *this; } |
---|
| 67 | const MyInt& operator--() { --val; return *this; } |
---|
| 68 | bool operator< (const MyInt& rhs) const { return val < rhs.val; } |
---|
| 69 | bool operator== (const MyInt& rhs) const { return val == rhs.val; } |
---|
| 70 | bool operator! () const { return !val; } |
---|
| 71 | friend std::istream& operator>>(std::istream&, MyInt&); |
---|
| 72 | friend std::ostream& operator<<(std::ostream&, const MyInt&); |
---|
| 73 | }; |
---|
| 74 | |
---|
| 75 | inline MyInt operator+(const MyInt& rhs) { return rhs; } |
---|
| 76 | inline MyInt operator-(const MyInt& rhs) { return MyInt(-rhs.val); } |
---|
| 77 | inline std::istream& operator>>(std::istream& is, MyInt& i) { is >> i.val; return is; } |
---|
| 78 | inline std::ostream& operator<<(std::ostream& os, const MyInt& i) { os << i.val; return os; } |
---|
| 79 | inline MyInt abs(MyInt rhs) { if (rhs < MyInt()) rhs = -rhs; return rhs; } |
---|
| 80 | |
---|
| 81 | // Test statistics |
---|
| 82 | static unsigned int total_count; |
---|
| 83 | static unsigned int error_count; |
---|
| 84 | |
---|
| 85 | // Check a specific assertion |
---|
| 86 | void Check(bool ok, const char *s, int line) |
---|
| 87 | { |
---|
| 88 | ++total_count; |
---|
| 89 | if (!ok) |
---|
| 90 | { |
---|
| 91 | std::cout << "Failed test " << s << " at line " << line << '\n'; |
---|
| 92 | ++error_count; |
---|
| 93 | } |
---|
| 94 | #ifdef SHOW_SUCCESSES |
---|
| 95 | std::cout << "Passed test " << s << '\n'; |
---|
| 96 | #endif |
---|
| 97 | } |
---|
| 98 | |
---|
| 99 | #define CHECK(x) Check((x), #x, __LINE__) |
---|
| 100 | |
---|
| 101 | // The basic test suite |
---|
| 102 | void run_tests() |
---|
| 103 | { |
---|
| 104 | typedef INT_TYPE IntType; |
---|
| 105 | typedef boost::rational<IntType> rat; |
---|
| 106 | |
---|
| 107 | error_count = 0; |
---|
| 108 | total_count = 0; |
---|
| 109 | |
---|
| 110 | /* gcd tests */ |
---|
| 111 | CHECK(( boost::gcd<IntType>(1,-1) == 1 )); |
---|
| 112 | CHECK(( boost::gcd<IntType>(-1,1) == 1 )); |
---|
| 113 | CHECK(( boost::gcd<IntType>(1,1) == 1 )); |
---|
| 114 | CHECK(( boost::gcd<IntType>(-1,-1) == 1 )); |
---|
| 115 | CHECK(( boost::gcd<IntType>(0,0) == 0 )); |
---|
| 116 | CHECK(( boost::gcd<IntType>(7,0) == 7 )); |
---|
| 117 | CHECK(( boost::gcd<IntType>(0,9) == 9 )); |
---|
| 118 | CHECK(( boost::gcd<IntType>(-7,0) == 7 )); |
---|
| 119 | CHECK(( boost::gcd<IntType>(0,-9) == 9 )); |
---|
| 120 | CHECK(( boost::gcd<IntType>(42,30) == 6 )); |
---|
| 121 | CHECK(( boost::gcd<IntType>(6,-9) == 3 )); |
---|
| 122 | CHECK(( boost::gcd<IntType>(-10,-10) == 10 )); |
---|
| 123 | CHECK(( boost::gcd<IntType>(-25,-10) == 5 )); |
---|
| 124 | |
---|
| 125 | /* lcm tests */ |
---|
| 126 | CHECK(( boost::lcm<IntType>(1,-1) == 1 )); |
---|
| 127 | CHECK(( boost::lcm<IntType>(-1,1) == 1 )); |
---|
| 128 | CHECK(( boost::lcm<IntType>(1,1) == 1 )); |
---|
| 129 | CHECK(( boost::lcm<IntType>(-1,-1) == 1 )); |
---|
| 130 | CHECK(( boost::lcm<IntType>(0,0) == 0 )); |
---|
| 131 | CHECK(( boost::lcm<IntType>(6,0) == 0 )); |
---|
| 132 | CHECK(( boost::lcm<IntType>(0,7) == 0 )); |
---|
| 133 | CHECK(( boost::lcm<IntType>(-5,0) == 0 )); |
---|
| 134 | CHECK(( boost::lcm<IntType>(0,-4) == 0 )); |
---|
| 135 | CHECK(( boost::lcm<IntType>(18,30) == 90 )); |
---|
| 136 | CHECK(( boost::lcm<IntType>(-6,9) == 18 )); |
---|
| 137 | CHECK(( boost::lcm<IntType>(-10,-10) == 10 )); |
---|
| 138 | CHECK(( boost::lcm<IntType>(25,-10) == 50 )); |
---|
| 139 | |
---|
| 140 | /* initialization tests */ |
---|
| 141 | rat r1, r2(0), r3(1), r4(-3), r5(7,2), r6(5,15), r7(14,-21), |
---|
| 142 | r8(-4,6), r9(-14,-70); |
---|
| 143 | |
---|
| 144 | CHECK(( r1.numerator() == 0 )); |
---|
| 145 | CHECK(( r2.numerator() == 0 )); |
---|
| 146 | CHECK(( r3.numerator() == 1 )); |
---|
| 147 | CHECK(( r4.numerator() == -3 )); |
---|
| 148 | CHECK(( r5.numerator() == 7 )); |
---|
| 149 | CHECK(( r6.numerator() == 1 )); |
---|
| 150 | CHECK(( r7.numerator() == -2 )); |
---|
| 151 | CHECK(( r8.numerator() == -2 )); |
---|
| 152 | CHECK(( r9.numerator() == 1 )); |
---|
| 153 | |
---|
| 154 | CHECK(( r1.denominator() == 1 )); |
---|
| 155 | CHECK(( r2.denominator() == 1 )); |
---|
| 156 | CHECK(( r3.denominator() == 1 )); |
---|
| 157 | CHECK(( r4.denominator() == 1 )); |
---|
| 158 | CHECK(( r5.denominator() == 2 )); |
---|
| 159 | CHECK(( r6.denominator() == 3 )); |
---|
| 160 | CHECK(( r7.denominator() == 3 )); |
---|
| 161 | CHECK(( r8.denominator() == 3 )); |
---|
| 162 | CHECK(( r9.denominator() == 5 )); |
---|
| 163 | |
---|
| 164 | /* assign() tests */ |
---|
| 165 | r1.assign(6,8); |
---|
| 166 | CHECK(( r1.numerator() == 3 )); |
---|
| 167 | CHECK(( r1.denominator() == 4 )); |
---|
| 168 | r1.assign(0,-7); |
---|
| 169 | CHECK(( r1.numerator() == 0 )); |
---|
| 170 | CHECK(( r1.denominator() == 1 )); |
---|
| 171 | |
---|
| 172 | /* comparison tests */ |
---|
| 173 | CHECK(( r1 == r2 )); |
---|
| 174 | CHECK(( r2 != r3 )); |
---|
| 175 | CHECK(( r4 < r3 )); |
---|
| 176 | CHECK(( r4 <= r5 )); |
---|
| 177 | CHECK(( r1 <= r2 )); |
---|
| 178 | CHECK(( r5 > r6 )); |
---|
| 179 | CHECK(( r5 >= r6 )); |
---|
| 180 | CHECK(( r7 >= r8 )); |
---|
| 181 | |
---|
| 182 | CHECK(( !(r3 == r2) )); |
---|
| 183 | CHECK(( !(r1 != r2) )); |
---|
| 184 | CHECK(( !(r1 < r2) )); |
---|
| 185 | CHECK(( !(r5 < r6) )); |
---|
| 186 | CHECK(( !(r9 <= r2) )); |
---|
| 187 | CHECK(( !(r8 > r7) )); |
---|
| 188 | CHECK(( !(r8 > r2) )); |
---|
| 189 | CHECK(( !(r4 >= r6) )); |
---|
| 190 | |
---|
| 191 | CHECK(( r1 == 0 )); |
---|
| 192 | CHECK(( r2 != -1 )); |
---|
| 193 | CHECK(( r3 < 2 )); |
---|
| 194 | CHECK(( r4 <= -3 )); |
---|
| 195 | CHECK(( r5 > 3 )); |
---|
| 196 | CHECK(( r6 >= 0 )); |
---|
| 197 | |
---|
| 198 | CHECK(( 0 == r2 )); |
---|
| 199 | CHECK(( 0 != r7 )); |
---|
| 200 | CHECK(( -1 < r8 )); |
---|
| 201 | CHECK(( -2 <= r9 )); |
---|
| 202 | CHECK(( 1 > r1 )); |
---|
| 203 | CHECK(( 1 >= r3 )); |
---|
| 204 | |
---|
| 205 | /* increment/decrement tests */ |
---|
| 206 | CHECK(( r1++ == r2 )); |
---|
| 207 | CHECK(( r1 != r2 )); |
---|
| 208 | CHECK(( r1 == r3 )); |
---|
| 209 | CHECK(( --r1 == r2 )); |
---|
| 210 | CHECK(( r8-- == r7 )); |
---|
| 211 | CHECK(( r8 != r7 )); |
---|
| 212 | CHECK(( ++r8 == r7 )); |
---|
| 213 | |
---|
| 214 | /* abs tests */ |
---|
| 215 | #ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP |
---|
| 216 | // This is a nasty hack, required because some compilers do not implement |
---|
| 217 | // "Koenig Lookup". Basically, if I call abs(r), the C++ standard says that |
---|
| 218 | // the compiler should look for a definition of abs in the namespace which |
---|
| 219 | // contains r's class (in this case boost) - among other places. |
---|
| 220 | |
---|
| 221 | using boost::abs; |
---|
| 222 | #endif |
---|
| 223 | |
---|
| 224 | CHECK(( abs(r2) == r2 )); |
---|
| 225 | CHECK(( abs(r5) == r5 )); |
---|
| 226 | CHECK(( abs(r8) == rat(2,3) )); |
---|
| 227 | |
---|
| 228 | /* addition/subtraction tests */ |
---|
| 229 | CHECK(( rat(1,2) + rat(1,2) == 1 )); |
---|
| 230 | CHECK(( rat(11,3) + rat(1,2) == rat(25,6) )); |
---|
| 231 | CHECK(( rat(-8,3) + rat(1,5) == rat(-37,15) )); |
---|
| 232 | CHECK(( rat(-7,6) + rat(1,7) == rat(1,7) - rat(7,6) )); |
---|
| 233 | CHECK(( rat(13,5) - rat(1,2) == rat(21,10) )); |
---|
| 234 | CHECK(( rat(22,3) + 1 == rat(25,3) )); |
---|
| 235 | CHECK(( rat(12,7) - 2 == rat(-2,7) )); |
---|
| 236 | CHECK(( 3 + rat(4,5) == rat(19,5) )); |
---|
| 237 | CHECK(( 4 - rat(9,2) == rat(-1,2) )); |
---|
| 238 | rat r0(11); |
---|
| 239 | r0 -= rat(20,3); |
---|
| 240 | CHECK(( r0 == rat(13,3) )); |
---|
| 241 | r0 += rat(1,2); |
---|
| 242 | CHECK(( r0 == rat(29,6) )); |
---|
| 243 | r0 -= 5; |
---|
| 244 | CHECK(( r0 == rat(1,-6) )); |
---|
| 245 | r0 += rat(1,5); |
---|
| 246 | CHECK(( r0 == rat(1,30) )); |
---|
| 247 | r0 += 2; |
---|
| 248 | CHECK(( r0 == rat(61,30) )); |
---|
| 249 | |
---|
| 250 | /* assignment tests */ |
---|
| 251 | r0 = rat(1,10); |
---|
| 252 | CHECK(( r0 == rat(1,10) )); |
---|
| 253 | r0 = -9; |
---|
| 254 | CHECK(( r0 == rat(-9,1) )); |
---|
| 255 | |
---|
| 256 | /* unary operator tests */ |
---|
| 257 | CHECK(( +r5 == r5 )); |
---|
| 258 | CHECK(( -r3 != r3 )); |
---|
| 259 | CHECK(( -(-r3) == r3 )); |
---|
| 260 | CHECK(( -r4 == 3 )); |
---|
| 261 | CHECK(( !r2 )); |
---|
| 262 | CHECK(( !!r3 )); |
---|
| 263 | |
---|
| 264 | /* multiplication tests */ |
---|
| 265 | CHECK(( rat(1,3) * rat(-3,4) == rat(-1,4) )); |
---|
| 266 | CHECK(( rat(2,5) * 7 == rat(14,5) )); |
---|
| 267 | CHECK(( -2 * rat(1,6) == rat(-1,3) )); |
---|
| 268 | r0 = rat(3,7); |
---|
| 269 | r0 *= 14; |
---|
| 270 | CHECK(( r0 == 6 )); |
---|
| 271 | r0 *= rat(3,8); |
---|
| 272 | CHECK(( r0 == rat(9,4) )); |
---|
| 273 | |
---|
| 274 | /* division tests */ |
---|
| 275 | CHECK(( rat(-1,20) / rat(4,5) == rat(-1,16) )); |
---|
| 276 | CHECK(( rat(5,6) / 7 == rat(5,42) )); |
---|
| 277 | CHECK(( 8 / rat(2,7) == 28 )); |
---|
| 278 | r0 = rat(4,3); |
---|
| 279 | r0 /= rat(5,4); |
---|
| 280 | CHECK(( r0 == rat(16,15) )); |
---|
| 281 | r0 /= 4; |
---|
| 282 | CHECK(( r0 == rat(4,15) )); |
---|
| 283 | CHECK(( rat(-1)/rat(-3) == rat(1,3) )); |
---|
| 284 | |
---|
| 285 | /* tests for operations on self */ |
---|
| 286 | r0 = rat(4,3); |
---|
| 287 | r0 += r0; |
---|
| 288 | CHECK(( r0 == rat(8,3) )); |
---|
| 289 | r0 *= r0; |
---|
| 290 | CHECK(( r0 == rat(64,9) )); |
---|
| 291 | r0 /= r0; |
---|
| 292 | CHECK(( r0 == rat(1,1) )); |
---|
| 293 | r0 -= r0; |
---|
| 294 | CHECK(( r0 == rat(0,1) )); |
---|
| 295 | |
---|
| 296 | /* operator<< and operator>> tests */ |
---|
| 297 | #ifndef BOOST_NO_STRINGSTREAM |
---|
| 298 | std::ostringstream oss; |
---|
| 299 | |
---|
| 300 | oss << rat(44,14); |
---|
| 301 | CHECK(( oss.str() == "22/7" )); |
---|
| 302 | typedef std::istringstream input_string_stream; |
---|
| 303 | #else |
---|
| 304 | std::ostrstream oss; |
---|
| 305 | oss << rat(44,14) << char(); |
---|
| 306 | auto unfreezer unfreeze(oss); |
---|
| 307 | CHECK(( !std::strcmp(oss.str(), "22/7") )); |
---|
| 308 | typedef std::istrstream input_string_stream; |
---|
| 309 | #endif |
---|
| 310 | { |
---|
| 311 | input_string_stream iss(""); |
---|
| 312 | iss >> r0; |
---|
| 313 | CHECK(( !iss )); |
---|
| 314 | } |
---|
| 315 | { |
---|
| 316 | input_string_stream iss("42"); |
---|
| 317 | iss >> r0; |
---|
| 318 | CHECK(( !iss )); |
---|
| 319 | } |
---|
| 320 | { |
---|
| 321 | input_string_stream iss("57A"); |
---|
| 322 | iss >> r0; |
---|
| 323 | CHECK(( !iss )); |
---|
| 324 | } |
---|
| 325 | { |
---|
| 326 | input_string_stream iss("20-20"); |
---|
| 327 | iss >> r0; |
---|
| 328 | CHECK(( !iss )); |
---|
| 329 | } |
---|
| 330 | { |
---|
| 331 | input_string_stream iss("1/"); |
---|
| 332 | iss >> r0; |
---|
| 333 | CHECK(( !iss )); |
---|
| 334 | } |
---|
| 335 | { |
---|
| 336 | input_string_stream iss("1/ 2"); |
---|
| 337 | iss >> r0; |
---|
| 338 | CHECK(( !iss )); |
---|
| 339 | } |
---|
| 340 | { |
---|
| 341 | input_string_stream iss("1 /2"); |
---|
| 342 | iss >> r0; |
---|
| 343 | CHECK(( !iss )); |
---|
| 344 | } |
---|
| 345 | { |
---|
| 346 | int n; |
---|
| 347 | input_string_stream iss("1/2 12"); |
---|
| 348 | CHECK(( iss >> r0 >> n )); |
---|
| 349 | CHECK(( r0 == rat(1,2) )); |
---|
| 350 | CHECK(( n == 12 )); |
---|
| 351 | } |
---|
| 352 | { |
---|
| 353 | input_string_stream iss("34/67"); |
---|
| 354 | CHECK(( iss >> r0 )); |
---|
| 355 | CHECK(( r0 == rat(34,67) )); |
---|
| 356 | } |
---|
| 357 | { |
---|
| 358 | input_string_stream iss("-3/-6"); |
---|
| 359 | CHECK(( iss >> r0 )); |
---|
| 360 | CHECK(( r0 == rat(1,2) )); |
---|
| 361 | } |
---|
| 362 | |
---|
| 363 | /* rational cast tests */ |
---|
| 364 | /* Note that these are not generic. The problem is that rational_cast<T> |
---|
| 365 | * requires a conversion from IntType to T. However, for a user-defined |
---|
| 366 | * IntType, it is not possible to define such a conversion except as |
---|
| 367 | * an operator T(). This causes problems with overloading resolution. |
---|
| 368 | */ |
---|
| 369 | { |
---|
| 370 | boost::rational<int> half(1,2); |
---|
| 371 | CHECK(( boost::rational_cast<double>(half) == 0.5 )); |
---|
| 372 | CHECK(( boost::rational_cast<int>(half) == 0 )); |
---|
| 373 | } |
---|
| 374 | |
---|
| 375 | /* End of main tests. */ |
---|
| 376 | |
---|
| 377 | /* mean number of times a fair 6-sided die must be thrown |
---|
| 378 | until each side has appeared at least once */ |
---|
| 379 | r0 = IntType(0); |
---|
| 380 | for (int i=1; i<=6; ++i) |
---|
| 381 | r0 += rat(1,i); |
---|
| 382 | r0 *= 6; |
---|
| 383 | CHECK(( r0 == rat(147,10) )); |
---|
| 384 | } |
---|
| 385 | |
---|
| 386 | } // namespace |
---|
| 387 | |
---|
| 388 | // Macro hacking: STR(INT_TYPE) gives the integer type defined by the user *as |
---|
| 389 | // a string* for reporting below... |
---|
| 390 | #define STR(x) STR2(x) |
---|
| 391 | #define STR2(x) #x |
---|
| 392 | |
---|
| 393 | int main() |
---|
| 394 | { |
---|
| 395 | std::cout << "Running tests for boost::rational<" STR(INT_TYPE) ">\n\n"; |
---|
| 396 | |
---|
| 397 | std::cout << "Implementation issue: the minimal size for a rational\n" |
---|
| 398 | << "is twice the size of the underlying integer type.\n\n" |
---|
| 399 | << "Checking to see if space is being wasted.\n"; |
---|
| 400 | std::cout << " sizeof(" STR(INT_TYPE) ") == " |
---|
| 401 | << sizeof(INT_TYPE) << "\n"; |
---|
| 402 | std::cout << " sizeof(boost::rational<" STR(INT_TYPE) ">) == " |
---|
| 403 | << sizeof(boost::rational<INT_TYPE>) << "\n\n"; |
---|
| 404 | if (sizeof(boost::rational<INT_TYPE>) > 2 * sizeof(INT_TYPE)) |
---|
| 405 | std::cout << "Implementation has included padding bytes\n\n"; |
---|
| 406 | else |
---|
| 407 | std::cout << "Implementation has minimal size\n\n"; |
---|
| 408 | |
---|
| 409 | try |
---|
| 410 | { |
---|
| 411 | run_tests(); |
---|
| 412 | } |
---|
| 413 | catch ( ... ) |
---|
| 414 | { |
---|
| 415 | std::cout << "Unexpected exception!\n"; |
---|
| 416 | return EXIT_FAILURE; |
---|
| 417 | } |
---|
| 418 | |
---|
| 419 | unsigned int success_count = total_count - error_count; |
---|
| 420 | unsigned int pct = 100 * success_count / total_count; |
---|
| 421 | std::cout << success_count << "/" << total_count << " tests succeeded (" |
---|
| 422 | << pct << "%). \n"; |
---|
| 423 | return error_count; |
---|
| 424 | } |
---|