From 171a9fa1611a2004491ef8d0113c2a89e6bfe148 Mon Sep 17 00:00:00 2001 From: dehnert Date: Sun, 25 Nov 2012 21:14:33 +0100 Subject: [PATCH] Started refactoring bit vector class. --- src/vector/bitvector.h | 185 ++++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 68 deletions(-) diff --git a/src/vector/bitvector.h b/src/vector/bitvector.h index 334b5ba78..b11e62544 100644 --- a/src/vector/bitvector.h +++ b/src/vector/bitvector.h @@ -6,26 +6,27 @@ #include #include "boost/integer/integer_mask.hpp" -#include -#include - #include "src/exceptions/invalid_state.h" #include "src/exceptions/invalid_argument.h" #include "src/exceptions/out_of_range.h" -#include +#include + +#include "log4cplus/logger.h" +#include "log4cplus/loggingmacros.h" + +extern log4cplus::Logger logger; namespace mrmc { namespace vector { -//! A Boolean Array -/*! - A bit vector for boolean fields or quick selection schemas on Matrix entries. - Does NOT perform index bound checks! +/*! + * A bit vector that is internally represented by an array of 64-bit values. */ class BitVector { +public: class constIndexIterator { constIndexIterator(uint_fast64_t* bucketPtr, uint_fast64_t* endBucketPtr) : bucketPtr(bucketPtr), endBucketPtr(endBucketPtr), offset(0), currentBitInByte(0) { } constIndexIterator& operator++() { @@ -50,68 +51,83 @@ class BitVector { uint_fast8_t currentBitInByte; }; -public: //! Constructor /*! - \param initial_length The initial size of the boolean Array. Can be changed later on via BitVector::resize() + * Constructs a bit vector which can hold the given number of bits. + * @param initialLength The number of bits the bit vector should be able to hold. */ - BitVector(uint_fast64_t initial_length) { - bucket_count = initial_length >> 6; - if ((initial_length & mod64mask) != 0) { - ++bucket_count; + BitVector(uint_fast64_t initialLength) { + // Check whether the given length is valid. + if (initialLength == 0) { + throw mrmc::exceptions::invalid_argument("Trying to create a bit vector of size 0."); } - bucket_array = new uint_fast64_t[bucket_count](); - // init all 0 - for (uint_fast64_t i = 0; i < bucket_count; ++i) { - bucket_array[i] = 0; + // Compute the correct number of buckets needed to store the given number of bits + bucket_count = initialLength >> 6; + if ((initialLength & mod64mask) != 0) { + ++bucket_count; } + // Finally, create the full bucket array. This should initialize the array + // with 0s (notice the parentheses at the end) for standard conforming + // compilers. + bucket_array = new uint_fast64_t[bucket_count](); } //! Copy Constructor /*! - Copy Constructor. Creates an exact copy of the source bit vector bv. Modification of either bit vector does not affect the other. - @param bv A reference to the bit vector that should be copied from + * Copy Constructor. Performs a deep copy of the given bit vector. + * @param bv A reference to the bit vector to be copied. */ - BitVector(const BitVector &bv) : - bucket_count(bv.bucket_count) { - pantheios::log_DEBUG("BitVector::CopyCTor: Using Copy() Ctor."); - bucket_array = new uint_fast64_t[bucket_count](); - memcpy(bucket_array, bv.bucket_array, - sizeof(uint_fast64_t) * bucket_count); + BitVector(const BitVector &bv) : bucket_count(bv.bucket_count) { + bucket_array = new uint_fast64_t[bucket_count]; + memcpy(bucket_array, bv.bucket_array, sizeof(uint_fast64_t) * bucket_count); } + //! Destructor + /*! + * Destructor. Frees the underlying bucket array. + */ ~BitVector() { - if (bucket_array != NULL) { + if (bucket_array != nullptr) { delete[] bucket_array; } } - void resize(uint_fast64_t new_length) { - uint_fast64_t* tempArray = new uint_fast64_t[new_length](); + /*! + * Resizes the bit vector to hold the given new number of bits. + * @param newLength The new number of bits the bit vector can hold. + */ + void resize(uint_fast64_t newLength) { + uint_fast64_t newBucketCount = newLength >> 6; + if ((newLength & mod64mask) != 0) { + ++bucket_count; + } + + // Reserve a temporary array for copying. + uint_fast64_t* tempArray = new uint_fast64_t[newBucketCount]; - // 64 bit/entries per uint_fast64_t - uint_fast64_t copySize = - (new_length <= (bucket_count << 6)) ? - (new_length >> 6) : (bucket_count); + // Copy over the elements from the old bit vector. + uint_fast64_t copySize = (newBucketCount <= bucket_count) ? newBucketCount : bucket_count; memcpy(tempArray, bucket_array, sizeof(uint_fast64_t) * copySize); - bucket_count = new_length >> 6; - if ((new_length & mod64mask) != 0) { - ++bucket_count; + // Initialize missing values in the new bit vector. + for (uint_fast64_t i = copySize; i < bucket_count; ++i) { + bucket_array[i] = 0; } + // Dispose of the old bit vector and set the new one. delete[] bucket_array; bucket_array = tempArray; } + /*! + * Sets the given truth value at the given index. + * @param index The index where to set the truth value. + * @param value The truth value to set. + */ void set(const uint_fast64_t index, const bool value) { uint_fast64_t bucket = index >> 6; - // Taking the step with mask is crucial as we NEED a 64bit shift, not a 32bit one. - // MSVC: C4334, use 1i64 or cast to __int64. - // result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) - uint_fast64_t mask = 1; - mask = mask << (index & mod64mask); + uint_fast64_t mask = static_cast(1) << (index & mod64mask); if (value) { bucket_array[bucket] |= mask; } else { @@ -119,21 +135,27 @@ public: } } + /*! + * Retrieves the truth value at the given index. + * @param index The index from which to retrieve the truth value. + */ bool get(const uint_fast64_t index) { uint_fast64_t bucket = index >> 6; - // Taking the step with mask is crucial as we NEED a 64bit shift, not a 32bit one. - // MSVC: C4334, use 1i64 or cast to __int64. - // result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) - uint_fast64_t mask = 1; - mask = mask << (index & mod64mask); + uint_fast64_t mask = static_cast(1) << (index & mod64mask); return ((bucket_array[bucket] & mask) == mask); } - // Operators + /*! + * Performs a logical "and" with the given bit vector. In case the sizes of the bit vectors + * do not match, only the matching portion is considered and the overlapping bits + * are set to 0. + * @param bv A reference to the bit vector to use for the operation. + * @return A bit vector corresponding to the logical "and" of the two bit vectors. + */ BitVector operator &(BitVector const &bv) { - uint_fast64_t minSize = - (bv.bucket_count < this->bucket_count) ? - bv.bucket_count : this->bucket_count; + uint_fast64_t minSize = (bv.bucket_count < this->bucket_count) ? bv.bucket_count : this->bucket_count; + + // Create resulting bit vector and perform the operation on the individual elements. BitVector result(minSize << 6); for (uint_fast64_t i = 0; i < minSize; ++i) { result.bucket_array[i] = this->bucket_array[i] & bv.bucket_array[i]; @@ -141,10 +163,18 @@ public: return result; } + + /*! + * Performs a logical "or" with the given bit vector. In case the sizes of the bit vectors + * do not match, only the matching portion is considered and the overlapping bits + * are set to 0. + * @param bv A reference to the bit vector to use for the operation. + * @return A bit vector corresponding to the logical "or" of the two bit vectors. + */ BitVector operator |(BitVector const &bv) { - uint_fast64_t minSize = - (bv.bucket_count < this->bucket_count) ? - bv.bucket_count : this->bucket_count; + uint_fast64_t minSize = (bv.bucket_count < this->bucket_count) ? bv.bucket_count : this->bucket_count; + + // Create resulting bit vector and perform the operation on the individual elements. BitVector result(minSize << 6); for (uint_fast64_t i = 0; i < minSize; ++i) { result.bucket_array[i] = this->bucket_array[i] | bv.bucket_array[i]; @@ -153,10 +183,17 @@ public: return result; } + /*! + * Performs a logical "xor" with the given bit vector. In case the sizes of the bit vectors + * do not match, only the matching portion is considered and the overlapping bits + * are set to 0. + * @param bv A reference to the bit vector to use for the operation. + * @return A bit vector corresponding to the logical "xor" of the two bit vectors. + */ BitVector operator ^(BitVector const &bv) { - uint_fast64_t minSize = - (bv.bucket_count < this->bucket_count) ? - bv.bucket_count : this->bucket_count; + uint_fast64_t minSize = (bv.bucket_count < this->bucket_count) ? bv.bucket_count : this->bucket_count; + + // Create resulting bit vector and perform the operation on the individual elements. BitVector result(minSize << 6); for (uint_fast64_t i = 0; i < minSize; ++i) { result.bucket_array[i] = this->bucket_array[i] ^ bv.bucket_array[i]; @@ -165,7 +202,12 @@ public: return result; } + /*! + * Performs a logical "not" on the bit vector. + * @return A bit vector corresponding to the logical "not" of the bit vector. + */ BitVector operator ~() { + // Create resulting bit vector and perform the operation on the individual elements. BitVector result(this->bucket_count << 6); for (uint_fast64_t i = 0; i < this->bucket_count; ++i) { result.bucket_array[i] = ~this->bucket_array[i]; @@ -174,16 +216,18 @@ public: return result; } + /*! + * Performs a logical "implies" with the given bit vector. In case the sizes of the bit vectors + * do not match, only the matching portion is considered and the overlapping bits + * are set to 0. + * @param bv A reference to the bit vector to use for the operation. + * @return A bit vector corresponding to the logical "xor" of the two bit vectors. + */ BitVector implies(BitVector& other) { - if (other.getSize() != this->getSize()) { - pantheios::log_ERROR( - "BitVector::implies: Throwing invalid argument exception for connecting bit vectors of different size."); - throw mrmc::exceptions::invalid_argument(); - } + // Create resulting bit vector and perform the operation on the individual elements. BitVector result(this->bucket_count << 6); for (uint_fast64_t i = 0; i < this->bucket_count; ++i) { - result.bucket_array[i] = ~this->bucket_array[i] - | other.bucket_array[i]; + result.bucket_array[i] = ~this->bucket_array[i] | other.bucket_array[i]; } return result; @@ -196,7 +240,8 @@ public: uint_fast64_t getNumberOfSetBits() { uint_fast64_t set_bits = 0; for (uint_fast64_t i = 0; i < bucket_count; ++i) { -#ifdef __GNUG__ // check if we are using g++ and use built-in function if available + // Check if we are using g++ or clang++ and, if so, use the built-in function +#if (defined (__GNUG__) || defined(__clang__)) set_bits += __builtin_popcountll(this->bucket_array[i]); #else uint_fast32_t cnt; @@ -210,8 +255,11 @@ public: return set_bits; } + /*! + * Retrieves the number of bits this bit vector can store. + */ uint_fast64_t getSize() { - return bucket_count; + return bucket_count << 6; } /*! @@ -223,13 +271,14 @@ public: } private: + /*! The number of 64-bit buckets we use as internal storage. */ uint_fast64_t bucket_count; - /*! Array containing the boolean bits for each node, 64bits/64nodes per element */ - uint_fast64_t* bucket_array; + /*! Array of 64-bit buckets to store the bits. */ + uint64_t* bucket_array; + /*! A bit mask that can be used to reduce a modulo operation to a logical "and". */ static const uint_fast64_t mod64mask = (1 << 6) - 1; - }; } // namespace vector