diff --git a/src/Magnum/CMakeLists.txt b/src/Magnum/CMakeLists.txt index 49be7a307..f38bf29d2 100644 --- a/src/Magnum/CMakeLists.txt +++ b/src/Magnum/CMakeLists.txt @@ -111,11 +111,11 @@ set(MagnumMath_SRCS Math/Angle.cpp Math/Color.cpp Math/Half.cpp - Math/Functions.cpp Math/Packing.cpp Math/instantiation.cpp) set(MagnumMath_GracefulAssert_SRCS + Math/Functions.cpp Math/PackingBatch.cpp) # Objects shared between main and math test library diff --git a/src/Magnum/Math/Functions.cpp b/src/Magnum/Math/Functions.cpp index b547cc12a..14fa68df5 100644 --- a/src/Magnum/Math/Functions.cpp +++ b/src/Magnum/Math/Functions.cpp @@ -23,6 +23,8 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include "Functions.h" namespace Magnum { namespace Math { @@ -41,4 +43,20 @@ UnsignedInt log2(UnsignedInt number) { return log; } + +UnsignedLong binomialCoefficient(UnsignedInt n, UnsignedInt k) { + if (k > n) return 0; + if (k * 2 > n) + k = n-k; + if (k == 0) return 1; + + UnsignedLong result = n; + for(UnsignedInt i = 2; i <= k; ++i ) { + CORRADE_ASSERT(result < ~UnsignedLong{} / (n-i+1), "Math::binomialCoefficient(): overflow for (" << Corrade::Utility::Debug::nospace << n << "choose" << k << Corrade::Utility::Debug::nospace << ")", 0ul); + result *= (n-i+1); + result /= i; + } + return result; +} + }} diff --git a/src/Magnum/Math/Functions.h b/src/Magnum/Math/Functions.h index 880a3d36a..2fe2cf1fc 100644 --- a/src/Magnum/Math/Functions.h +++ b/src/Magnum/Math/Functions.h @@ -34,7 +34,6 @@ #include /* std::div() */ #include #include -#include #include #include "Magnum/visibility.h" @@ -413,30 +412,15 @@ template inline Vector ceil(const Vector inline typename std::enable_if::value, T>::type binomialCoefficient(T n, T k) { - CORRADE_ASSERT(k >= 0, "k must be positive to compute the Binomial Coefficient", T{0}); - if (k > n) return 0; - if (k * 2 > n) - k = n-k; - if (k == 0) return 1; - - T result = n; - for(T i = 2; i <= k; ++i ) { - CORRADE_ASSERT(result < std::numeric_limits::max()/(n-i+1), "Overflow In Binomial Coefficient", T{0}); - result *= (n-i+1); - result /= i; - } - return result; -} +UnsignedLong MAGNUM_EXPORT binomialCoefficient(UnsignedInt n, UnsignedInt k); /** @brief Floating point division remainder diff --git a/src/Magnum/Math/Test/FunctionsTest.cpp b/src/Magnum/Math/Test/FunctionsTest.cpp index 2718f183f..465400145 100644 --- a/src/Magnum/Math/Test/FunctionsTest.cpp +++ b/src/Magnum/Math/Test/FunctionsTest.cpp @@ -54,6 +54,7 @@ struct FunctionsTest: Corrade::TestSuite::Tester { void fmod(); void binomialCoefficient(); + void binomialCoefficientOverflow(); void sqrt(); void sqrtInverted(); @@ -118,6 +119,7 @@ FunctionsTest::FunctionsTest() { &FunctionsTest::fmod, &FunctionsTest::binomialCoefficient, + &FunctionsTest::binomialCoefficientOverflow, &FunctionsTest::sqrt, &FunctionsTest::sqrtInverted, @@ -298,12 +300,24 @@ void FunctionsTest::ceil() { } void FunctionsTest::binomialCoefficient() { - CORRADE_COMPARE(Math::binomialCoefficient(1, 1), 1); - CORRADE_COMPARE(Math::binomialCoefficient(1, 0), 1); - CORRADE_COMPARE(Math::binomialCoefficient(19, 11), 75582); - CORRADE_COMPARE(Math::binomialCoefficient(1000, 999), 1000); - CORRADE_COMPARE(Math::binomialCoefficient(0, 0), 1); - CORRADE_COMPARE(Math::binomialCoefficient(32, 11), 129024480); + CORRADE_COMPARE(Math::binomialCoefficient(1, 1), 1ul); + CORRADE_COMPARE(Math::binomialCoefficient(1, 0), 1ul); + CORRADE_COMPARE(Math::binomialCoefficient(19, 11), 75582ul); + CORRADE_COMPARE(Math::binomialCoefficient(1000, 999), 1000ul); + CORRADE_COMPARE(Math::binomialCoefficient(0, 0), 1ul); + CORRADE_COMPARE(Math::binomialCoefficient(32, 11), 129024480ul); + CORRADE_COMPARE(Math::binomialCoefficient(62, 31), 465428353255261088ul); +} + +void FunctionsTest::binomialCoefficientOverflow() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + Math::binomialCoefficient(63, 31); + CORRADE_COMPARE(out.str(), "Math::binomialCoefficient(): overflow for (63 choose 31)\n"); } void FunctionsTest::fmod() {