From 9de4717f0eeb42ac232b4f7683c54b37f6d15784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 26 Sep 2020 21:46:15 +0200 Subject: [PATCH] Math: add popcount(). --- doc/changelog.dox | 1 + src/Magnum/Math/Functions.cpp | 24 +++++++++++++++++++++ src/Magnum/Math/Functions.h | 30 ++++++++++++++++++++++++++ src/Magnum/Math/Test/FunctionsTest.cpp | 17 +++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/doc/changelog.dox b/doc/changelog.dox index 53720142c..b497bf280 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -70,6 +70,7 @@ See also: - Added @ref Math::fmod() (see [mosra/magnum#454](https://github.com/mosra/magnum/pull/454)) - Added @ref Math::binomialCoefficient() (see [mosra/magnum#461](https://github.com/mosra/magnum/pull/461)) +- Added @ref Math::popcount() @subsubsection changelog-latest-new-meshtools MeshTools library diff --git a/src/Magnum/Math/Functions.cpp b/src/Magnum/Math/Functions.cpp index 736625bbd..20132eedb 100644 --- a/src/Magnum/Math/Functions.cpp +++ b/src/Magnum/Math/Functions.cpp @@ -30,6 +30,30 @@ namespace Magnum { namespace Math { +#if !defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) +namespace { + +/* https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + there's also https://stackoverflow.com/a/109025 which mostly just copies + parts of this together with a lot of noise and syntax errors. */ +template inline UnsignedInt popcountImplementation(T v) { + v = v - ((v >> 1) & ~T(0)/3); + v = (v & ~T(0)/15*3) + ((v >> 2) & ~T(0)/15*3); + v = (v + (v >> 4)) & ~T(0)/255*15; + return (v*(~T(0)/255)) >> (sizeof(T) - 1)*8; +} + +} + +UnsignedInt popcount(UnsignedInt number) { + return popcountImplementation(number); +} + +UnsignedInt popcount(UnsignedLong number) { + return popcountImplementation(number); +} +#endif + UnsignedInt log(UnsignedInt base, UnsignedInt number) { UnsignedInt log = 0; while(number /= base) diff --git a/src/Magnum/Math/Functions.h b/src/Magnum/Math/Functions.h index f0d0216ba..9f0be0026 100644 --- a/src/Magnum/Math/Functions.h +++ b/src/Magnum/Math/Functions.h @@ -93,6 +93,36 @@ time, with @f$ n \ge k \ge 0 @f$: @f[ */ UnsignedLong MAGNUM_EXPORT binomialCoefficient(UnsignedInt n, UnsignedInt k); +/** +@brief Count of bits set in a number +@m_since_latest + +Expands to `__builtin_popcount` / `__builtin_popcountll` on GCC and Clang, uses +the [Counting bits set, in parallel](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel) +implementation from Sean Eron Anderson Bit Twiddling Hacks page on MSVC and +elsewhere. +*/ +/* Explicitly checking for Clang in addition to GCC to catch also clang-cl */ +#if defined(CORRADE_TARGET_GCC) || defined(CORRADE_TARGET_CLANG) +inline UnsignedInt popcount(UnsignedInt number) { + return __builtin_popcount(number); +} +#else +MAGNUM_EXPORT UnsignedInt popcount(UnsignedInt number); +#endif + +/** +@overload +@m_since_latest +*/ +#if defined(CORRADE_TARGET_GCC) || defined(CORRADE_TARGET_CLANG) +inline UnsignedInt popcount(UnsignedLong number) { + return __builtin_popcountll(number); +} +#else +MAGNUM_EXPORT UnsignedInt popcount(UnsignedLong number); +#endif + /** @{ @name Trigonometric functions diff --git a/src/Magnum/Math/Test/FunctionsTest.cpp b/src/Magnum/Math/Test/FunctionsTest.cpp index 815b75b65..561876c4e 100644 --- a/src/Magnum/Math/Test/FunctionsTest.cpp +++ b/src/Magnum/Math/Test/FunctionsTest.cpp @@ -36,6 +36,8 @@ namespace Magnum { namespace Math { namespace Test { namespace { struct FunctionsTest: Corrade::TestSuite::Tester { explicit FunctionsTest(); + template void popcount(); + void powIntegral(); void pow(); @@ -99,6 +101,10 @@ typedef Math::Vector3 Vector3b; typedef Math::Vector3 Vector3i; FunctionsTest::FunctionsTest() { + addRepeatedTests({ + &FunctionsTest::popcount, + &FunctionsTest::popcount}, 8); + addTests({&FunctionsTest::powIntegral, &FunctionsTest::pow, @@ -154,6 +160,17 @@ FunctionsTest::FunctionsTest() { }); } +template void FunctionsTest::popcount() { + setTestCaseTemplateName(TypeTraits::name()); + + /* Trivial cases */ + CORRADE_COMPARE(Math::popcount(T(0)), 0); + CORRADE_COMPARE(Math::popcount(~T{}), sizeof(T)*8); + + /* 0x101101011101000110010100 */ + CORRADE_COMPARE(Math::popcount(T(0xb5d194) << testCaseRepeatId()), 12); +} + void FunctionsTest::powIntegral() { CORRADE_COMPARE(Math::pow<10>(2ul), 1024ul); CORRADE_COMPARE(Math::pow<0>(3ul), 1ul);