From d38303505dda14d73a739623730da12529ce512f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 25 May 2019 01:17:27 +0200 Subject: [PATCH] Math: added batch isInf() and isNan(). --- doc/changelog.dox | 1 + src/Magnum/Math/Functions.h | 6 +- src/Magnum/Math/FunctionsBatch.h | 68 +++++++++++++++++++++ src/Magnum/Math/Test/FunctionsBatchTest.cpp | 52 +++++++++++++++- 4 files changed, 124 insertions(+), 3 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index f7c5c4d42..3d15114d8 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -126,6 +126,7 @@ See also: - New @ref Math::IsScalar, @ref Math::IsVector, @ref Math::IsIntegral, @ref Math::IsFloatingPoint and @ref Math::IsUnitless type traits and a @ref Math::UnderlyingTypeOf utility +- Added batch versions of @ref Math::isInf() and @ref Math::isNan() @subsubsection changelog-latest-new-platform Platform libraries diff --git a/src/Magnum/Math/Functions.h b/src/Magnum/Math/Functions.h index a6c70a7f6..69f65268c 100644 --- a/src/Magnum/Math/Functions.h +++ b/src/Magnum/Math/Functions.h @@ -160,7 +160,8 @@ the operations component-wise. /** @brief If given number is a positive or negative infinity -@see @ref isNan(), @ref Constants::inf() +@see @ref isNan(), @ref Constants::inf(), + @ref isInf(Corrade::Containers::ArrayView) */ template inline typename std::enable_if::value, bool>::type isInf(T value) { return std::isinf(UnderlyingTypeOf(value)); @@ -178,7 +179,8 @@ template inline BoolVector isInf(const Vector) */ template inline typename std::enable_if::value, bool>::type isNan(T value) { return std::isnan(UnderlyingTypeOf(value)); diff --git a/src/Magnum/Math/FunctionsBatch.h b/src/Magnum/Math/FunctionsBatch.h index 45baab2e6..0dd5a93ef 100644 --- a/src/Magnum/Math/FunctionsBatch.h +++ b/src/Magnum/Math/FunctionsBatch.h @@ -43,6 +43,74 @@ These functions process an ubounded range of values, as opposed to single vectors or scalars. */ +/** +@brief If any number in the range is a positive or negative infinity + +For scalar types returns @cpp true @ce as soon as it finds an infinite value, +@cpp false @ce otherwise. For vector types, returns @ref BoolVector with bits +set to @cpp 1 @ce if any value has that component infinite. If the range is +empty, returns @cpp false @ce or a @ref BoolVector with no bits set. +@see @ref isInf(T), @ref Constants::inf() +*/ +template auto isInf(Corrade::Containers::ArrayView range) -> decltype(isInf(std::declval())) { + if(range.empty()) return {}; + + /* For scalars, this loop exits once any value is infinity. For vectors + the loop accumulates the bits and exits as soon as all bits are set + or the input is exhausted */ + auto out = isInf(range[0]); /* bool or BoolVector */ + for(std::size_t i = 1; i != range.size(); ++i) { + if(out) break; + out = out || isInf(range[i]); + } + + return out; +} + +/** @overload */ +template inline auto isInf(std::initializer_list list) -> decltype(isInf(std::declval())) { + return isInf(Corrade::Containers::arrayView(list.begin(), list.size())); +} + +/** @overload */ +template inline auto isInf(const T(&array)[size]) -> decltype(isInf(std::declval())) { + return isInf(Corrade::Containers::arrayView(array)); +} + +/** +@brief If any number in the range is a NaN + +For scalar types returns @cpp true @ce as soon as it finds a NaN value, +@cpp false @ce otherwise. For vector types, returns @ref BoolVector with bits +set to @cpp 1 @ce if any value has that component NaN. If the range is empty, +returns @cpp false @ce or a @ref BoolVector with no bits set. +@see @ref isNan(T), @ref Constants::nan() +*/ +template inline auto isNan(Corrade::Containers::ArrayView range) -> decltype(isNan(std::declval())) { + if(range.empty()) return {}; + + /* For scalars, this loop exits once any value is infinity. For vectors + the loop accumulates the bits and exits as soon as all bits are set + or the input is exhausted */ + auto out = isNan(range[0]); /* bool or BoolVector */ + for(std::size_t i = 1; i != range.size(); ++i) { + if(out) break; + out = out || isNan(range[i]); + } + + return out; +} + +/** @overload */ +template inline auto isNan(std::initializer_list list) -> decltype(isInf(std::declval())) { + return isNan(Corrade::Containers::arrayView(list.begin(), list.size())); +} + +/** @overload */ +template inline bool isNan(const T(&array)[size]) { + return isNan(Corrade::Containers::arrayView(array)); +} + /** @brief Minimum of a range diff --git a/src/Magnum/Math/Test/FunctionsBatchTest.cpp b/src/Magnum/Math/Test/FunctionsBatchTest.cpp index c86946c40..6ba6f4f8c 100644 --- a/src/Magnum/Math/Test/FunctionsBatchTest.cpp +++ b/src/Magnum/Math/Test/FunctionsBatchTest.cpp @@ -33,6 +33,9 @@ namespace Magnum { namespace Math { namespace Test { namespace { struct FunctionsBatchTest: Corrade::TestSuite::Tester { explicit FunctionsBatchTest(); + void isInf(); + void isNan(); + void min(); void max(); void minmax(); @@ -40,15 +43,62 @@ struct FunctionsBatchTest: Corrade::TestSuite::Tester { using namespace Literals; +typedef Math::Constants Constants; typedef Math::Vector2 Vector2; typedef Math::Vector3 Vector3i; +typedef Math::Vector3 Vector3; FunctionsBatchTest::FunctionsBatchTest() { - addTests({&FunctionsBatchTest::min, + addTests({&FunctionsBatchTest::isInf, + &FunctionsBatchTest::isNan, + + &FunctionsBatchTest::min, &FunctionsBatchTest::max, &FunctionsBatchTest::minmax}); } +void FunctionsBatchTest::isInf() { + CORRADE_VERIFY(!Math::isInf({5.0f, -2.0f, 9.0f})); + CORRADE_VERIFY(Math::isInf({5.0f, Constants::inf(), 9.0f})); + + CORRADE_COMPARE(Math::isInf({Vector2{5.0f, -3.0f}, + Vector2{-2.0f, 14.0f}, + Vector2{9.0f, -5.0f}}), BoolVector<2>{0}); + CORRADE_COMPARE(Math::isInf({Vector2{5.0f, -3.0f}, + Vector2{-2.0f, 14.0f}, + Vector2{Constants::inf(), -5.0f}}), BoolVector<2>{1}); + + CORRADE_VERIFY(!Math::isInf(std::initializer_list{})); + CORRADE_COMPARE(Math::isInf(std::initializer_list{}), BoolVector<3>{0}); + + const Float a[]{5.0f, -2.0f, -Constants::inf()}; + CORRADE_VERIFY(Math::isInf(a)); + + const Float b[]{5.0f, -2.0f, -1.0}; + CORRADE_VERIFY(!Math::isInf(b)); +} + +void FunctionsBatchTest::isNan() { + CORRADE_VERIFY(!Math::isNan({5.0f, -2.0f, 9.0f})); + CORRADE_VERIFY(Math::isNan({5.0f, 9.0f, Constants::nan()})); + + CORRADE_COMPARE(Math::isNan({Vector2{5.0f, -3.0f}, + Vector2{-2.0f, 14.0f}, + Vector2{9.0f, -5.0f}}), BoolVector<2>{0}); + CORRADE_COMPARE(Math::isNan({Vector2{5.0f, -3.0f}, + Vector2{14.0f, Constants::nan()}, + Vector2{-2.0f, -5.0f}}), BoolVector<2>{2}); + + CORRADE_VERIFY(!Math::isNan(std::initializer_list{})); + CORRADE_COMPARE(Math::isNan(std::initializer_list{}), BoolVector<3>{0}); + + const Float a[]{5.0f, -Constants::nan(), -2.0f}; + CORRADE_VERIFY(Math::isNan(a)); + + const Float b[]{5.0f, -2.0f, -1.0}; + CORRADE_VERIFY(!Math::isNan(b)); +} + void FunctionsBatchTest::min() { CORRADE_COMPARE(Math::min({5, -2, 9}), -2); CORRADE_COMPARE(Math::min({Vector3i(5, -3, 2),