From dcb4595e6e6f7ce1fc709dc0f772c5ba1bbb040f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 27 Jan 2013 00:49:16 +0100 Subject: [PATCH] Math: new class BoolVector. Will handle output from various comparison operators in Vector. --- src/Math/BoolVector.h | 232 +++++++++++++++++++++++++++++++ src/Math/CMakeLists.txt | 1 + src/Math/Test/BoolVectorTest.cpp | 166 ++++++++++++++++++++++ src/Math/Test/CMakeLists.txt | 1 + 4 files changed, 400 insertions(+) create mode 100644 src/Math/BoolVector.h create mode 100644 src/Math/Test/BoolVectorTest.cpp diff --git a/src/Math/BoolVector.h b/src/Math/BoolVector.h new file mode 100644 index 000000000..eb472a64f --- /dev/null +++ b/src/Math/BoolVector.h @@ -0,0 +1,232 @@ +#ifndef Magnum_Math_BoolVector_h +#define Magnum_Math_BoolVector_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Math::BoolVector + */ + +#include +#include + +namespace Magnum { namespace Math { + +/** +@brief %Vector storing boolean values +@tparam size Bit count + +Result of component-wise comparison from Vector. The boolean values are stored +as bits in array of unsigned bytes, unused bits have undefined value which +doesn't affect comparison or all() / none() / any() functions. See also +@ref matrix-vector for brief introduction. +*/ +template class BoolVector { + static_assert(size != 0, "BoolVector cannot have zero elements"); + + public: + static const std::size_t Size = size; /**< @brief %Vector size */ + static const std::size_t DataSize = (size-1)/8+1; /**< @brief %Vector storage size */ + + /** @brief Construct boolean with one value for all elements */ + inline static BoolVector from(bool value) { + BoolVector out; + + for(std::size_t i = 0; i != size; ++i) + out._data[i] = (value ? FullSegmentMask : 0); + + return out; + } + + /** @brief Construct zero-filled boolean vector */ + inline constexpr BoolVector(): _data() {} + + /** @brief Construct boolean vector from given values */ + template inline constexpr BoolVector(std::uint8_t first, U... next): _data{first, std::uint8_t(next)...} { + static_assert(sizeof...(next)+1 == DataSize, "Improper number of arguments passed to BoolVector constructor"); + } + + /** @brief Copy constructor */ + inline constexpr BoolVector(const BoolVector&) = default; + + /** @brief Copy assignment */ + inline BoolVector& operator=(const BoolVector&) = default; + + /** + * @brief Raw data + * @return %Array of DataSize length + * + * @see operator[](), set() + */ + inline std::uint8_t* data() { return _data; } + inline constexpr const std::uint8_t* data() const { return _data; } /**< @overload */ + + /** @brief Bit at given position */ + inline constexpr bool operator[](std::size_t i) const { + return (_data[i/8] >> i%8) & 0x01; + } + + /** @brief Set bit at given position */ + inline BoolVector& set(std::size_t i, bool value) { + _data[i/8] |= ((value & 0x01) << i%8); + return *this; + } + + /** @brief Equality comparison */ + inline bool operator==(const BoolVector& other) const { + for(std::size_t i = 0; i != size/8; ++i) + if(_data[i] != other._data[i]) return false; + + /* Check last segment */ + if(size%8 && (_data[DataSize-1] & LastSegmentMask) != (other._data[DataSize-1] & LastSegmentMask)) + return false; + + return true; + } + + /** @brief Non-equality comparison */ + inline bool operator!=(const BoolVector& other) const { + return !operator==(other); + } + + /** @brief Whether all bits are set */ + bool all() const { + /* Check all full segments */ + for(std::size_t i = 0; i != size/8; ++i) + if(_data[i] != FullSegmentMask) return false; + + /* Check last segment */ + if(size%8 && (_data[DataSize-1] & LastSegmentMask) != LastSegmentMask) + return false; + + return true; + } + + /** @brief Whether no bits are set */ + bool none() const { + /* Check all full segments */ + for(std::size_t i = 0; i != size/8; ++i) + if(_data[i]) return false; + + /* Check last segment */ + if(size%8 && (_data[DataSize-1] & LastSegmentMask)) + return false; + + return true; + } + + /** @brief Whether any bit is set */ + inline bool any() const { + return !none(); + } + + /** @brief Bitwise inversion */ + inline BoolVector operator~() const { + BoolVector out; + + for(std::size_t i = 0; i != DataSize; ++i) + out._data[i] = ~_data[i]; + + return out; + } + + /** + * @brief Bitwise AND and assign + * + * The computation is done in-place. + */ + inline BoolVector& operator&=(const BoolVector& other) { + for(std::size_t i = 0; i != DataSize; ++i) + _data[i] &= other._data[i]; + + return *this; + } + + /** + * @brief Bitwise AND + * + * @see operator&=() + */ + inline BoolVector operator&(const BoolVector& other) const { + return BoolVector(*this) &= other; + } + + /** + * @brief Bitwise OR and assign + * + * The computation is done in-place. + */ + inline BoolVector& operator|=(const BoolVector& other) { + for(std::size_t i = 0; i != DataSize; ++i) + _data[i] |= other._data[i]; + + return *this; + } + + /** + * @brief Bitwise OR + * + * @see operator|=() + */ + inline BoolVector operator|(const BoolVector& other) const { + return BoolVector(*this) |= other; + } + + /** + * @brief Bitwise XOR and assign + * + * The computation is done in-place. + */ + inline BoolVector& operator^=(const BoolVector& other) { + for(std::size_t i = 0; i != DataSize; ++i) + _data[i] ^= other._data[i]; + + return *this; + } + + /** + * @brief Bitwise XOR + * + * @see operator^=() + */ + inline BoolVector operator^(const BoolVector& other) const { + return BoolVector(*this) ^= other; + } + + private: + enum: std::uint8_t { + FullSegmentMask = 0xFF, + LastSegmentMask = (1 << size%8) - 1 + }; + + std::uint8_t _data[(size-1)/8+1]; +}; + +/** @debugoperator{Magnum::Math::BoolVector} */ +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const BoolVector& value) { + debug << "BoolVector("; + debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); + for(std::size_t i = 0; i != size; ++i) { + if(i && !(i%8)) debug << " "; + debug << (value[i] ? "1" : "0"); + } + debug << ")"; + debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); + return debug; +} + +}} + +#endif diff --git a/src/Math/CMakeLists.txt b/src/Math/CMakeLists.txt index 1d347f4d0..284415193 100644 --- a/src/Math/CMakeLists.txt +++ b/src/Math/CMakeLists.txt @@ -1,4 +1,5 @@ set(MagnumMath_HEADERS + BoolVector.h Constants.h Functions.h Math.h diff --git a/src/Math/Test/BoolVectorTest.cpp b/src/Math/Test/BoolVectorTest.cpp new file mode 100644 index 000000000..b430f6d02 --- /dev/null +++ b/src/Math/Test/BoolVectorTest.cpp @@ -0,0 +1,166 @@ +/* + Copyright © 1910, 1911, 1912 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include +#include + +#include "Math/BoolVector.h" + +namespace Magnum { namespace Math { namespace Test { + +class BoolVectorTest: public Corrade::TestSuite::Tester { + public: + explicit BoolVectorTest(); + + void constructDefault(); + void constructOneValue(); + void data(); + + void compare(); + void compareUndefined(); + void all(); + void none(); + void any(); + + void bitInverse(); + void bitAndOrXor(); + + void debug(); +}; + +static_assert(BoolVector<15>::DataSize == 2, "Improper DataSize"); +static_assert(BoolVector<16>::DataSize == 2, "Improper DataSize"); +static_assert(BoolVector<17>::DataSize == 3, "Improper DataSize"); + +typedef Math::BoolVector<19> BoolVector19; + +BoolVectorTest::BoolVectorTest() { + addTests(&BoolVectorTest::constructDefault, + &BoolVectorTest::constructOneValue, + &BoolVectorTest::data, + + &BoolVectorTest::compare, + &BoolVectorTest::compareUndefined, + &BoolVectorTest::all, + &BoolVectorTest::none, + &BoolVectorTest::any, + + &BoolVectorTest::bitInverse, + &BoolVectorTest::bitAndOrXor, + + &BoolVectorTest::debug); +} + +void BoolVectorTest::constructDefault() { + CORRADE_COMPARE(BoolVector19(), BoolVector19(0x00, 0x00, 0x00)); +} + +void BoolVectorTest::constructOneValue() { + CORRADE_COMPARE(BoolVector19::from(false), BoolVector19(0x00, 0x00, 0x00)); + CORRADE_COMPARE(BoolVector19::from(true), BoolVector19(0xff, 0xff, 0x07)); +} + +void BoolVectorTest::data() { + BoolVector19 a(0x08, 0x03, 0x04); + + CORRADE_VERIFY(!a[0] && !a[1] && !a[2]); + CORRADE_VERIFY(a[3]); + CORRADE_VERIFY(!a[4] && !a[5] && !a[6] && !a[7]); + CORRADE_VERIFY(a[8]); + CORRADE_VERIFY(a[9]); + CORRADE_VERIFY(!a[10] && !a[11] && !a[12] && !a[13] && !a[14] && !a[15] && !a[16] && !a[17]); + CORRADE_VERIFY(a[18]); + + a.set(15, true); + CORRADE_VERIFY(a[15]); + + CORRADE_COMPARE(a, BoolVector19(0x08, 0x83, 0x04)); +} + +void BoolVectorTest::compare() { + BoolVector19 a(0xa5, 0x5f, 0x07); + CORRADE_VERIFY(a == a); + + /* Change in full segments */ + BoolVector19 b(0xa3, 0x5f, 0x07); + BoolVector19 c(0xa5, 0x98, 0x07); + CORRADE_VERIFY(a != b); + CORRADE_VERIFY(a != c); + + /* Change in last bit */ + BoolVector19 d(0xa5, 0x5f, 0x06); + CORRADE_VERIFY(a != d); +} + +void BoolVectorTest::compareUndefined() { + BoolVector19 a(0xa5, 0x5f, 0x07); + + /* Change in unused part of last segment */ + BoolVector19 b(0xa5, 0x5f, 0x0f); + CORRADE_VERIFY(a == b); + + /* Change in used part of last segment */ + BoolVector19 c(0xa5, 0x5f, 0x03); + CORRADE_VERIFY(a != c); +} + +void BoolVectorTest::all() { + CORRADE_VERIFY(BoolVector19(0xff, 0xff, 0x07).all()); + + /* Last segment - bit in used and unused part */ + CORRADE_VERIFY(BoolVector19(0xff, 0xff, 0x0f).all()); + CORRADE_VERIFY(!BoolVector19(0xff, 0xff, 0x04).all()); +} + +void BoolVectorTest::none() { + CORRADE_VERIFY(BoolVector19(0x00, 0x00, 0x00).none()); + + /* Last segment - bit in used and unused part */ + CORRADE_VERIFY(BoolVector19(0x00, 0x00, 0x08).none()); + CORRADE_VERIFY(!BoolVector19(0x00, 0x00, 0x04).none()); +} + +void BoolVectorTest::any() { + CORRADE_VERIFY(BoolVector19(0x00, 0x01, 0x00).any()); + + /* Last segment - bit in used and unused part */ + CORRADE_VERIFY(BoolVector19(0x00, 0x00, 0x04).any()); + CORRADE_VERIFY(!BoolVector19(0x00, 0x00, 0x08).any()); +} + +void BoolVectorTest::bitInverse() { + CORRADE_COMPARE(~BoolVector19(0xa5, 0x5f, 0x03), BoolVector19(0x5a, 0xa0, 0x04)); +} + +void BoolVectorTest::bitAndOrXor() { + BoolVector19 a(0xa5, 0x5f, 0x03); + BoolVector19 b(0x37, 0xf3, 0x06); + + CORRADE_COMPARE(a & b, BoolVector19(0x25, 0x53, 0x02)); + CORRADE_COMPARE(a | b, BoolVector19(0xb7, 0xff, 0x07)); + CORRADE_COMPARE(a ^ b, BoolVector19(0x92, 0xac, 0x05)); +} + +void BoolVectorTest::debug() { + std::ostringstream o; + + Corrade::Utility::Debug(&o) << BoolVector19(0x25, 0x53, 0x02); + + CORRADE_COMPARE(o.str(), "BoolVector(10100100 11001010 010)\n"); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Math::Test::BoolVectorTest) diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index 185946510..1d413def9 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -1,3 +1,4 @@ +corrade_add_test(MathBoolVectorTest BoolVectorTest.cpp) corrade_add_test(MathConstantsTest ConstantsTest.cpp) corrade_add_test(MathFunctionsTest FunctionsTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathMathTypeTraitsTest MathTypeTraitsTest.cpp)