Line data Source code
1 : // Copyright (c) 2011-2023 Made to Order Software Corp. All Rights Reserved 2 : // 3 : // https://snapwebsites.org/project/as2js 4 : // contact@m2osw.com 5 : // 6 : // This program is free software: you can redistribute it and/or modify 7 : // it under the terms of the GNU General Public License as published by 8 : // the Free Software Foundation, either version 3 of the License, or 9 : // (at your option) any later version. 10 : // 11 : // This program is distributed in the hope that it will be useful, 12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 : // GNU General Public License for more details. 15 : // 16 : // You should have received a copy of the GNU General Public License 17 : // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 : 19 : // as2js 20 : // 21 : #include <as2js/floating_point.h> 22 : 23 : 24 : // self 25 : // 26 : #include "catch_main.h" 27 : 28 : 29 : // C 30 : // 31 : #include <cstring> 32 : #include <algorithm> 33 : 34 : 35 : // last include 36 : // 37 : #include <snapdev/poison.h> 38 : 39 : 40 : #pragma GCC diagnostic ignored "-Wfloat-equal" 41 : 42 : 43 : 44 : 45 3 : CATCH_TEST_CASE("floating_point", "[number][floating_point][type]") 46 : { 47 3 : CATCH_START_SECTION("floating_point: default constructor") 48 : { 49 : // default constructor gives us exactly zero 50 : // 51 1 : as2js::floating_point zero; 52 1 : CATCH_REQUIRE(zero.get() == 0.0); 53 : } 54 3 : CATCH_END_SECTION() 55 : 56 3 : CATCH_START_SECTION("floating_point: basics with float") 57 : { 58 : // float constructor, copy constructor, copy assignment 59 : // 60 1001 : for(int i(0); i < 1000; ++i) 61 : { 62 : // generate a random 64 bit number 63 : // 64 1000 : float s1(rand() & 1 ? -1.0f : 1.0f); 65 1000 : std::int64_t rnd(0); 66 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 67 1000 : float n1(static_cast<float>(rnd)); 68 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 69 1000 : while(rnd == 0) // denominator should not be zero 70 : { 71 0 : SNAP_CATCH2_NAMESPACE::random(rnd); 72 : } 73 1000 : float d1(static_cast<float>(rnd)); 74 1000 : float r(n1 / d1 * s1); 75 1000 : as2js::floating_point random(r); 76 1000 : CATCH_REQUIRE(random.get() == r); 77 1000 : CATCH_REQUIRE(!random.is_nan()); 78 1000 : CATCH_REQUIRE(!random.is_infinity()); 79 1000 : CATCH_REQUIRE(!random.is_positive_infinity()); 80 1000 : CATCH_REQUIRE(!random.is_negative_infinity()); 81 1000 : CATCH_REQUIRE(random.classified_infinity() == 0); 82 : 83 1000 : as2js::floating_point copy(random); 84 1000 : CATCH_REQUIRE(copy.get() == r); 85 1000 : CATCH_REQUIRE(!copy.is_nan()); 86 1000 : CATCH_REQUIRE(!copy.is_infinity()); 87 1000 : CATCH_REQUIRE(!copy.is_positive_infinity()); 88 1000 : CATCH_REQUIRE(!copy.is_negative_infinity()); 89 1000 : CATCH_REQUIRE(copy.classified_infinity() == 0); 90 : 91 1000 : float s2(rand() & 1 ? -1.0f : 1.0f); 92 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 93 1000 : float n2(static_cast<float>(rnd)); 94 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 95 1000 : while(rnd == 0) // denominator should not be zero 96 : { 97 0 : SNAP_CATCH2_NAMESPACE::random(rnd); 98 : } 99 1000 : float d2(static_cast<float>(rnd)); 100 1000 : float q(n2 / d2 * s2); 101 : 102 1000 : random = q; 103 1000 : CATCH_REQUIRE(random.get() == q); 104 1000 : CATCH_REQUIRE(!random.is_nan()); 105 1000 : CATCH_REQUIRE(!random.is_infinity()); 106 1000 : CATCH_REQUIRE(!random.is_positive_infinity()); 107 1000 : CATCH_REQUIRE(!random.is_negative_infinity()); 108 1000 : CATCH_REQUIRE(random.classified_infinity() == 0); 109 : 110 12000 : for(int j(0); j <= 10; ++j) 111 : { 112 : // 1.0, 0.1, 0.01, ... 0.000000001 113 11000 : double const epsilon(pow(10.0, static_cast<double>(-j))); 114 : 115 11000 : bool nearly_equal(false); 116 : { 117 11000 : as2js::floating_point::value_type const diff = fabs(random.get() - copy.get()); 118 11000 : if(random.get() == 0.0 119 11000 : || copy.get() == 0.0 120 22000 : || diff < std::numeric_limits<double>::min()) 121 : { 122 0 : nearly_equal = diff < (epsilon * std::numeric_limits<double>::min()); 123 : } 124 : else 125 : { 126 11000 : nearly_equal = diff / (fabs(random.get()) + fabs(copy.get())) < epsilon; 127 : } 128 : } 129 : 130 11000 : CATCH_REQUIRE(as2js::compare_utils::is_ordered(random.compare(copy))); 131 11000 : CATCH_REQUIRE(as2js::compare_utils::is_ordered(copy.compare(random))); 132 11000 : if(q < r) 133 : { 134 5379 : CATCH_REQUIRE(random.compare(copy) == as2js::compare_t::COMPARE_LESS); 135 5379 : CATCH_REQUIRE(copy.compare(random) == as2js::compare_t::COMPARE_GREATER); 136 5379 : CATCH_REQUIRE(!(random.nearly_equal(copy, epsilon) ^ nearly_equal)); 137 5379 : CATCH_REQUIRE(!(copy.nearly_equal(random, epsilon) ^ nearly_equal)); 138 : } 139 5621 : else if(q > r) 140 : { 141 5621 : CATCH_REQUIRE(random.compare(copy) == as2js::compare_t::COMPARE_GREATER); 142 5621 : CATCH_REQUIRE(copy.compare(random) == as2js::compare_t::COMPARE_LESS); 143 5621 : CATCH_REQUIRE(!(random.nearly_equal(copy, epsilon) ^ nearly_equal)); 144 5621 : CATCH_REQUIRE(!(copy.nearly_equal(random, epsilon) ^ nearly_equal)); 145 : } 146 : else 147 : { 148 0 : CATCH_REQUIRE(random.compare(copy) == as2js::compare_t::COMPARE_EQUAL); 149 0 : CATCH_REQUIRE(copy.compare(random) == as2js::compare_t::COMPARE_EQUAL); 150 0 : CATCH_REQUIRE(random.nearly_equal(copy, epsilon)); 151 0 : CATCH_REQUIRE(copy.nearly_equal(random, epsilon)); 152 : } 153 : } 154 : 155 1000 : float s3(rand() & 1 ? -1.0f : 1.0f); 156 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 157 1000 : float n3(static_cast<float>(rnd)); 158 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 159 1000 : while(rnd == 0) // denominator should not be zero 160 : { 161 0 : SNAP_CATCH2_NAMESPACE::random(rnd); 162 : } 163 1000 : float d3(static_cast<float>(rnd)); 164 1000 : float p(n3 / d3 * s3); 165 : 166 1000 : random.set(p); 167 1000 : CATCH_REQUIRE(random.get() == p); 168 1000 : CATCH_REQUIRE(!random.is_nan()); 169 1000 : CATCH_REQUIRE(!random.is_infinity()); 170 1000 : CATCH_REQUIRE(!random.is_positive_infinity()); 171 1000 : CATCH_REQUIRE(!random.is_negative_infinity()); 172 1000 : CATCH_REQUIRE(random.classified_infinity() == 0); 173 : } 174 : } 175 3 : CATCH_END_SECTION() 176 : 177 3 : CATCH_START_SECTION("floating_point: basics with double") 178 : { 179 : // double constructor, copy constructor, copy assignment 180 1001 : for(int i(0); i < 1000; ++i) 181 : { 182 : // generate a random 64 bit number 183 1000 : double s1(rand() & 1 ? -1.0 : 1.0); 184 1000 : std::int64_t rnd(0); 185 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 186 1000 : double n1(static_cast<double>(rnd)); 187 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 188 1000 : while(rnd == 0) // denominator should not be zero 189 : { 190 0 : SNAP_CATCH2_NAMESPACE::random(rnd); 191 : } 192 : 193 1000 : double d1(static_cast<double>(rnd)); 194 1000 : double r(n1 / d1 * s1); 195 : 196 1000 : as2js::floating_point random(r); 197 1000 : CATCH_REQUIRE(random.get() == r); 198 1000 : CATCH_REQUIRE(!random.is_nan()); 199 1000 : CATCH_REQUIRE(!random.is_infinity()); 200 1000 : CATCH_REQUIRE(!random.is_positive_infinity()); 201 1000 : CATCH_REQUIRE(!random.is_negative_infinity()); 202 1000 : CATCH_REQUIRE(random.get() != std::numeric_limits<double>::quiet_NaN()); 203 1000 : CATCH_REQUIRE(random.classified_infinity() == 0); 204 : 205 1000 : as2js::floating_point copy(random); 206 1000 : CATCH_REQUIRE(copy.get() == r); 207 1000 : CATCH_REQUIRE(!copy.is_nan()); 208 1000 : CATCH_REQUIRE(!copy.is_infinity()); 209 1000 : CATCH_REQUIRE(!copy.is_positive_infinity()); 210 1000 : CATCH_REQUIRE(!copy.is_negative_infinity()); 211 1000 : CATCH_REQUIRE(copy.get() != std::numeric_limits<double>::quiet_NaN()); 212 1000 : CATCH_REQUIRE(copy.classified_infinity() == 0); 213 : 214 1000 : double s2(rand() & 1 ? -1.0 : 1.0); 215 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 216 1000 : double n2(static_cast<double>(rnd)); 217 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 218 1000 : while(rnd == 0) // denominator should not be zero 219 : { 220 0 : SNAP_CATCH2_NAMESPACE::random(rnd); 221 : } 222 1000 : double d2(static_cast<double>(rnd)); 223 1000 : double q(n2 / d2 * s2); 224 : 225 1000 : random = q; 226 1000 : CATCH_REQUIRE(random.get() == q); 227 1000 : CATCH_REQUIRE(!random.is_nan()); 228 1000 : CATCH_REQUIRE(!random.is_infinity()); 229 1000 : CATCH_REQUIRE(!random.is_positive_infinity()); 230 1000 : CATCH_REQUIRE(!random.is_negative_infinity()); 231 1000 : CATCH_REQUIRE(random.get() != std::numeric_limits<double>::quiet_NaN()); 232 1000 : CATCH_REQUIRE(random.classified_infinity() == 0); 233 : 234 1000 : CATCH_REQUIRE(as2js::compare_utils::is_ordered(random.compare(copy))); 235 1000 : CATCH_REQUIRE(as2js::compare_utils::is_ordered(copy.compare(random))); 236 1000 : if(q < r) 237 : { 238 480 : CATCH_REQUIRE(random.compare(copy) == as2js::compare_t::COMPARE_LESS); 239 480 : CATCH_REQUIRE(copy.compare(random) == as2js::compare_t::COMPARE_GREATER); 240 : } 241 520 : else if(q > r) 242 : { 243 520 : CATCH_REQUIRE(random.compare(copy) == as2js::compare_t::COMPARE_GREATER); 244 520 : CATCH_REQUIRE(copy.compare(random) == as2js::compare_t::COMPARE_LESS); 245 : } 246 : else 247 : { 248 0 : CATCH_REQUIRE(random.compare(copy) == as2js::compare_t::COMPARE_EQUAL); 249 0 : CATCH_REQUIRE(copy.compare(random) == as2js::compare_t::COMPARE_EQUAL); 250 : } 251 : 252 1000 : double s3(rand() & 1 ? -1.0 : 1.0); 253 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 254 1000 : double n3(static_cast<double>(rnd)); 255 1000 : SNAP_CATCH2_NAMESPACE::random(rnd); 256 1000 : while(rnd == 0) // denominator should not be zero 257 : { 258 0 : SNAP_CATCH2_NAMESPACE::random(rnd); 259 : } 260 1000 : double d3(static_cast<double>(rnd)); 261 1000 : double p(n3 / d3 * s3); 262 : 263 1000 : random.set(p); 264 1000 : CATCH_REQUIRE(random.get() == p); 265 1000 : CATCH_REQUIRE(!random.is_nan()); 266 1000 : CATCH_REQUIRE(!random.is_infinity()); 267 1000 : CATCH_REQUIRE(!random.is_positive_infinity()); 268 1000 : CATCH_REQUIRE(!random.is_negative_infinity()); 269 1000 : CATCH_REQUIRE(random.get() != std::numeric_limits<double>::quiet_NaN()); 270 1000 : CATCH_REQUIRE(random.classified_infinity() == 0); 271 : } 272 : } 273 3 : CATCH_END_SECTION() 274 3 : } 275 : 276 : 277 1 : CATCH_TEST_CASE("floating_point_special_numbers", "[number][floating_point][type]") 278 : { 279 1 : CATCH_START_SECTION("floating_point: special numbers") 280 : { 281 1 : as2js::floating_point special; 282 : 283 : // start with zero 284 1 : CATCH_REQUIRE(special.get() == 0.0); 285 1 : CATCH_REQUIRE(special.nearly_equal(0.0)); 286 : 287 : // create a random number to compare with 288 1 : double s1(rand() & 1 ? -1.0 : 1.0); 289 1 : std::uint64_t rnd; 290 1 : SNAP_CATCH2_NAMESPACE::random(rnd); 291 1 : double n1(static_cast<double>(rnd)); 292 1 : SNAP_CATCH2_NAMESPACE::random(rnd); 293 1 : while(rnd == 0) // denominator should not be zero 294 : { 295 0 : SNAP_CATCH2_NAMESPACE::random(rnd); 296 : } 297 1 : double d1(static_cast<double>(rnd)); 298 1 : double p(n1 / d1 * s1); 299 1 : as2js::floating_point r(p); 300 : 301 : // test NaN 302 1 : special.set_nan(); 303 1 : CATCH_REQUIRE(special.is_nan()); 304 1 : CATCH_REQUIRE_FALSE(special.is_infinity()); 305 1 : CATCH_REQUIRE_FALSE(special.is_positive_infinity()); 306 1 : CATCH_REQUIRE_FALSE(special.is_negative_infinity()); 307 1 : CATCH_REQUIRE(special.get() != 0.0); 308 1 : CATCH_REQUIRE_FALSE(special.get() == p); 309 1 : CATCH_REQUIRE(special.get() != p); 310 1 : CATCH_REQUIRE_FALSE(special.get() > p); 311 1 : CATCH_REQUIRE_FALSE(special.get() >= p); 312 1 : CATCH_REQUIRE_FALSE(special.get() < p); 313 1 : CATCH_REQUIRE_FALSE(special.get() <= p); 314 : // We do not offer those yet 315 : //CATCH_REQUIRE(special != r); 316 : //CATCH_REQUIRE(!(special == r)); 317 : //CATCH_REQUIRE(!(special > r)); 318 : //CATCH_REQUIRE(!(special >= r)); 319 : //CATCH_REQUIRE(!(special < r)); 320 : //CATCH_REQUIRE(!(special <= r)); 321 1 : CATCH_REQUIRE(special.get() != std::numeric_limits<double>::quiet_NaN()); 322 1 : CATCH_REQUIRE(special.compare(p) == as2js::compare_t::COMPARE_UNORDERED); 323 1 : CATCH_REQUIRE(special.compare(r) == as2js::compare_t::COMPARE_UNORDERED); 324 1 : CATCH_REQUIRE(r.compare(special) == as2js::compare_t::COMPARE_UNORDERED); 325 1 : CATCH_REQUIRE(special.classified_infinity() == 0); 326 1 : CATCH_REQUIRE_FALSE(special.nearly_equal(p)); 327 1 : CATCH_REQUIRE_FALSE(special.nearly_equal(special)); 328 : 329 : // test Infinity 330 1 : special.set_infinity(); // +inf 331 1 : CATCH_REQUIRE_FALSE(special.is_nan()); 332 1 : CATCH_REQUIRE(special.is_infinity()); 333 1 : CATCH_REQUIRE(special.is_positive_infinity()); 334 1 : CATCH_REQUIRE_FALSE(special.is_negative_infinity()); 335 1 : CATCH_REQUIRE(special.get() != 0.0); 336 1 : CATCH_REQUIRE(special.get() != p); 337 1 : CATCH_REQUIRE_FALSE(special.get() == p); 338 1 : CATCH_REQUIRE(special.get() > p); 339 1 : CATCH_REQUIRE(special.get() >= p); 340 1 : CATCH_REQUIRE_FALSE(special.get() < p); 341 1 : CATCH_REQUIRE_FALSE(special.get() <= p); 342 : // We do not offer those yet 343 : //CATCH_REQUIRE(special != r); 344 : //CATCH_REQUIRE(!(special == r)); 345 : //CATCH_REQUIRE(!(special > r)); 346 : //CATCH_REQUIRE(!(special >= r)); 347 : //CATCH_REQUIRE(!(special < r)); 348 : //CATCH_REQUIRE(!(special <= r)); 349 1 : CATCH_REQUIRE(special.get() != std::numeric_limits<double>::quiet_NaN()); 350 1 : CATCH_REQUIRE(special.compare(p) == as2js::compare_t::COMPARE_GREATER); 351 1 : CATCH_REQUIRE(special.compare(r) == as2js::compare_t::COMPARE_GREATER); 352 1 : CATCH_REQUIRE(r.compare(special) == as2js::compare_t::COMPARE_LESS); 353 1 : CATCH_REQUIRE(special.classified_infinity() == 1); 354 1 : CATCH_REQUIRE_FALSE(special.nearly_equal(p)); 355 1 : CATCH_REQUIRE(special.nearly_equal(special)); 356 : 357 1 : as2js::floating_point pinf; 358 1 : pinf.set_infinity(); 359 1 : CATCH_REQUIRE(pinf.compare(special) == as2js::compare_t::COMPARE_EQUAL); 360 1 : CATCH_REQUIRE(special.compare(pinf) == as2js::compare_t::COMPARE_EQUAL); 361 : 362 1 : special.set(-special.get()); // -inf 363 1 : CATCH_REQUIRE_FALSE(special.is_nan()); 364 1 : CATCH_REQUIRE(special.is_infinity()); 365 1 : CATCH_REQUIRE_FALSE(special.is_positive_infinity()); 366 1 : CATCH_REQUIRE(special.is_negative_infinity()); 367 1 : CATCH_REQUIRE(special.get() != 0.0); 368 1 : CATCH_REQUIRE(special.get() != p); 369 1 : CATCH_REQUIRE_FALSE(special.get() == p); 370 1 : CATCH_REQUIRE_FALSE(special.get() > p); 371 1 : CATCH_REQUIRE_FALSE(special.get() >= p); 372 1 : CATCH_REQUIRE(special.get() < p); 373 1 : CATCH_REQUIRE(special.get() <= p); 374 : // We do not offer those yet 375 : //CATCH_REQUIRE(special != r); 376 : //CATCH_REQUIRE(!(special == r)); 377 : //CATCH_REQUIRE(!(special > r)); 378 : //CATCH_REQUIRE(!(special >= r)); 379 : //CATCH_REQUIRE(!(special < r)); 380 : //CATCH_REQUIRE(!(special <= r)); 381 1 : CATCH_REQUIRE(special.get() != std::numeric_limits<double>::quiet_NaN()); 382 1 : CATCH_REQUIRE(special.compare(p) == as2js::compare_t::COMPARE_LESS); 383 1 : CATCH_REQUIRE(special.compare(r) == as2js::compare_t::COMPARE_LESS); 384 1 : CATCH_REQUIRE(r.compare(special) == as2js::compare_t::COMPARE_GREATER); 385 1 : CATCH_REQUIRE(special.classified_infinity() == -1); 386 1 : CATCH_REQUIRE_FALSE(special.nearly_equal(p)); 387 1 : CATCH_REQUIRE(special.nearly_equal(special)); 388 : 389 1 : CATCH_REQUIRE(pinf.compare(special) != as2js::compare_t::COMPARE_EQUAL); 390 1 : CATCH_REQUIRE(special.compare(pinf) != as2js::compare_t::COMPARE_EQUAL); 391 1 : CATCH_REQUIRE_FALSE(pinf.nearly_equal(special)); 392 1 : CATCH_REQUIRE_FALSE(special.nearly_equal(pinf)); 393 : } 394 1 : CATCH_END_SECTION() 395 1 : } 396 : 397 : 398 5 : CATCH_TEST_CASE("floating_point_nearly_equal", "[number][floating_point][type][compare]") 399 : { 400 5 : CATCH_START_SECTION("floating_point_nearly_equal: exactly equal") 401 : { 402 : // exactly equal 403 : // 404 1 : as2js::floating_point f1(3.14159); 405 1 : as2js::floating_point f2(3.14159); 406 1 : CATCH_REQUIRE(f1.nearly_equal(f2)); 407 1 : CATCH_REQUIRE(f2.nearly_equal(f1)); 408 : 409 : // equal to self as well 410 : // 411 1 : CATCH_REQUIRE(f1.nearly_equal(f1)); 412 1 : CATCH_REQUIRE(f2.nearly_equal(f2)); 413 : } 414 5 : CATCH_END_SECTION() 415 : 416 5 : CATCH_START_SECTION("floating_point_nearly_equal: +/-1e-5") 417 : { 418 : // nearly equal at +/-1e-5 419 : // 420 1 : as2js::floating_point f1(3.14159); 421 1 : as2js::floating_point f2(3.14158); 422 1 : CATCH_REQUIRE(f1.nearly_equal(f2)); 423 1 : CATCH_REQUIRE(f2.nearly_equal(f1)); 424 : } 425 5 : CATCH_END_SECTION() 426 : 427 5 : CATCH_START_SECTION("floating_point_nearly_equal: +/-1e-6") 428 : { 429 : // nearly equal at +/-1e-6 430 : // 431 1 : as2js::floating_point f1(3.1415926); 432 1 : as2js::floating_point f2(3.1415936); 433 1 : CATCH_REQUIRE(f1.nearly_equal(f2)); 434 1 : CATCH_REQUIRE(f2.nearly_equal(f1)); 435 : } 436 5 : CATCH_END_SECTION() 437 : 438 5 : CATCH_START_SECTION("floating_point_nearly_equal: +/-1e-4") 439 : { 440 : // nearly equal at +/-1e-4 -- fails 441 : // 442 1 : as2js::floating_point f1(3.1415926); 443 1 : as2js::floating_point f2(3.1416926); 444 1 : CATCH_REQUIRE_FALSE(f1.nearly_equal(f2)); 445 1 : CATCH_REQUIRE_FALSE(f2.nearly_equal(f1)); 446 : } 447 5 : CATCH_END_SECTION() 448 : 449 5 : CATCH_START_SECTION("floating_point_nearly_equal: very different") 450 : { 451 : // nearly equal, very different 452 : { 453 1 : as2js::floating_point f1(3.1415926); 454 1 : as2js::floating_point f2(-3.1415926); 455 1 : CATCH_REQUIRE_FALSE(f1.nearly_equal(f2)); 456 1 : CATCH_REQUIRE_FALSE(f2.nearly_equal(f1)); 457 : } 458 : { 459 1 : as2js::floating_point f1(3.1415926); 460 1 : as2js::floating_point f2(0.0); 461 1 : CATCH_REQUIRE_FALSE(f1.nearly_equal(f2)); 462 1 : CATCH_REQUIRE_FALSE(f2.nearly_equal(f1)); 463 : } 464 : { 465 1 : as2js::floating_point f1(0.0); 466 1 : as2js::floating_point f2(3.1415926); 467 1 : CATCH_REQUIRE_FALSE(f1.nearly_equal(f2)); 468 1 : CATCH_REQUIRE_FALSE(f2.nearly_equal(f1)); 469 : } 470 : } 471 5 : CATCH_END_SECTION() 472 5 : } 473 : 474 : 475 : // vim: ts=4 sw=4 et