You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
820 lines
26 KiB
820 lines
26 KiB
#ifndef STORM_STORAGE_BITVECTOR_H_
|
|
#define STORM_STORAGE_BITVECTOR_H_
|
|
|
|
#include <exception>
|
|
#include <new>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
|
|
#include "src/storage/VectorSet.h"
|
|
|
|
#include "src/exceptions/InvalidStateException.h"
|
|
#include "src/exceptions/InvalidArgumentException.h"
|
|
#include "src/exceptions/OutOfRangeException.h"
|
|
|
|
#include "src/utility/OsDetection.h"
|
|
#include "src/utility/Hash.h"
|
|
#include "log4cplus/logger.h"
|
|
#include "log4cplus/loggingmacros.h"
|
|
|
|
extern log4cplus::Logger logger;
|
|
|
|
#include <iostream>
|
|
|
|
namespace storm {
|
|
|
|
namespace storage {
|
|
|
|
/*!
|
|
* A bit vector that is internally represented by an array of 64-bit values.
|
|
*/
|
|
class BitVector {
|
|
|
|
public:
|
|
/*!
|
|
* A class that enables iterating over the indices of the bit vector whose
|
|
* bits are set to true. Note that this is a const iterator, which cannot
|
|
* alter the bit vector.
|
|
*/
|
|
class constIndexIterator {
|
|
friend class BitVector;
|
|
public:
|
|
|
|
/*!
|
|
* Constructs a const iterator over the indices of the set bits in the
|
|
* given bit vector, starting and stopping, respectively, at the given
|
|
* indices.
|
|
* @param bitVector The bit vector to iterate over.
|
|
* @param startIndex The index where to begin looking for set bits.
|
|
* @param setOnFirstBit If set, upon construction, the current index is
|
|
* set to the first set bit in the bit vector.
|
|
* @param endIndex The number of elements to iterate over.
|
|
*/
|
|
constIndexIterator(const BitVector& bitVector, uint_fast64_t startIndex, uint_fast64_t endIndex, bool setOnFirstBit = true) : bitVector(bitVector), endIndex(endIndex) {
|
|
if (setOnFirstBit) {
|
|
currentIndex = bitVector.getNextSetIndex(startIndex, endIndex);
|
|
} else {
|
|
currentIndex = startIndex;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Increases the position of the iterator to the position of the next bit that
|
|
* is set to true.
|
|
* @return A reference to this iterator.
|
|
*/
|
|
constIndexIterator& operator++() {
|
|
currentIndex = bitVector.getNextSetIndex(++currentIndex, endIndex);
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
* Returns the index of the current bit that is set to true.
|
|
* @return The index of the current bit that is set to true.
|
|
*/
|
|
uint_fast64_t operator*() const {
|
|
return currentIndex;
|
|
}
|
|
|
|
/*!
|
|
* Compares the iterator with another iterator to determine whether
|
|
* the iteration process has reached the end.
|
|
*/
|
|
bool operator!=(const constIndexIterator& rhs) const {
|
|
return currentIndex != rhs.currentIndex;
|
|
}
|
|
|
|
private:
|
|
|
|
/*! The bit vector to search for set bits. */
|
|
const BitVector& bitVector;
|
|
|
|
/*! The index of the most recently discovered set bit. */
|
|
uint_fast64_t currentIndex;
|
|
|
|
/*!
|
|
* The index of the element past the end. Used for properly terminating
|
|
* the search for set bits.
|
|
*/
|
|
uint_fast64_t endIndex;
|
|
};
|
|
|
|
/*
|
|
* Standard constructor. Constructs an empty bit vector of length 0.
|
|
*/
|
|
BitVector() : bucketCount(0), bitCount(0), bucketArray(nullptr), endIterator(*this, 0, 0, false), truncateMask(0) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
/*!
|
|
* Constructs a bit vector which can hold the given number of bits and
|
|
* initializes all bits to the provided truth value.
|
|
* @param length The number of bits the bit vector should be able to hold.
|
|
* @param initTrue The initial value of the first |length| bits.
|
|
*/
|
|
BitVector(uint_fast64_t length, bool initTrue = false) : bitCount(length), endIterator(*this, length, length, false), truncateMask((1ll << (bitCount & mod64mask)) - 1ll) {
|
|
// Check whether the given length is valid.
|
|
if (length == 0) {
|
|
LOG4CPLUS_ERROR(logger, "Cannot create bit vector of size 0.");
|
|
throw storm::exceptions::InvalidArgumentException() << "Cannot create bit vector of size 0.";
|
|
}
|
|
|
|
// Compute the correct number of buckets needed to store the given number of bits
|
|
bucketCount = length >> 6;
|
|
if ((length & mod64mask) != 0) {
|
|
++bucketCount;
|
|
}
|
|
|
|
if (initTrue) {
|
|
// Finally, create the full bucket array.
|
|
this->bucketArray = new uint64_t[bucketCount];
|
|
|
|
// Now initialize the values.
|
|
for (uint_fast64_t i = 0; i < bucketCount; ++i) {
|
|
this->bucketArray[i] = -1ll;
|
|
}
|
|
|
|
truncateLastBucket();
|
|
} else {
|
|
// Finally, create the full bucket array.
|
|
this->bucketArray = new uint64_t[bucketCount]();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Creates a bit vector that has exactly those bits set that are defined by the given set.
|
|
*
|
|
* @param length The length of the bit vector to create.
|
|
* @param bitsToSet A set of indices whose bits to set in the bit vector.
|
|
* @param initTrue The initial value of the first |length| bits.
|
|
*/
|
|
BitVector(uint_fast64_t length, storm::storage::VectorSet<uint_fast64_t> const& bitsToSet, bool initTrue = false) : BitVector(length, initTrue) {
|
|
for (auto bit : bitsToSet) {
|
|
this->set(bit, true);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Creates a bit vector that has exactly the bits set that are given by the provided iterator range.
|
|
*
|
|
* @param The length of the bit vector.
|
|
* @param begin The begin of the iterator range.
|
|
* @param end The end of the iterator range.
|
|
*/
|
|
template<typename InputIterator>
|
|
BitVector(uint_fast64_t length, InputIterator begin, InputIterator end) : BitVector(length) {
|
|
for (InputIterator it = begin; it != end; ++it) {
|
|
this->set(*it, true);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* 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) : bucketCount(bv.bucketCount), bitCount(bv.bitCount), endIterator(*this, bitCount, bitCount, false), truncateMask((1ll << (bitCount & mod64mask)) - 1ll) {
|
|
LOG4CPLUS_DEBUG(logger, "Invoking copy constructor.");
|
|
bucketArray = new uint64_t[bucketCount];
|
|
std::copy(bv.bucketArray, bv.bucketArray + this->bucketCount, this->bucketArray);
|
|
}
|
|
|
|
/*!
|
|
* Copy Constructor. Performs a deep copy of the bits in the given bit vector that are given by the filter.
|
|
* @param bv A reference to the bit vector to be copied.
|
|
* @param filter The filter to apply for copying.
|
|
*/
|
|
BitVector(BitVector const& other, BitVector const& filter) : bitCount(filter.getNumberOfSetBits()), endIterator(*this, bitCount, bitCount, false), truncateMask((1ll << (bitCount & mod64mask)) - 1ll) {
|
|
// Determine the number of buckets we need for the given bit count and create bucket array.
|
|
bucketCount = bitCount >> 6;
|
|
if ((bitCount & mod64mask) != 0) {
|
|
++bucketCount;
|
|
}
|
|
this->bucketArray = new uint64_t[bucketCount]();
|
|
|
|
// Now copy over all bits given by the filter.
|
|
uint_fast64_t nextPosition = 0;
|
|
for (auto position : filter) {
|
|
this->set(nextPosition, other.get(position));
|
|
nextPosition++;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Move constructor. Move constructs the bit vector from the given bit vector.
|
|
*
|
|
*/
|
|
BitVector(BitVector&& bv) : bucketCount(bv.bucketCount), bitCount(bv.bitCount), bucketArray(bv.bucketArray), endIterator(*this, bitCount, bitCount, false), truncateMask((1ll << (bitCount & mod64mask)) - 1ll) {
|
|
LOG4CPLUS_DEBUG(logger, "Invoking move constructor.");
|
|
bv.bucketArray = nullptr;
|
|
}
|
|
|
|
/*!
|
|
* Destructor. Frees the underlying bucket array.
|
|
*/
|
|
~BitVector() {
|
|
if (this->bucketArray != nullptr) {
|
|
delete[] this->bucketArray;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Compares the given bit vector with the current one.
|
|
*/
|
|
bool operator==(BitVector const& bv) {
|
|
// If the lengths of the vectors do not match, they are considered unequal.
|
|
if (this->bitCount != bv.bitCount) return false;
|
|
|
|
// If the lengths match, we compare the buckets one by one.
|
|
for (uint_fast64_t index = 0; index < this->bucketCount; ++index) {
|
|
if (this->bucketArray[index] != bv.bucketArray[index]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// All buckets were equal, so the bit vectors are equal.
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* Assigns the given bit vector to the current bit vector by a deep copy.
|
|
* @param bv The bit vector to assign to the current bit vector.
|
|
* @return A reference to this bit vector after it has been assigned the
|
|
* given bit vector by means of a deep copy.
|
|
*/
|
|
BitVector& operator=(BitVector const& bv) {
|
|
LOG4CPLUS_DEBUG(logger, "Performing copy assignment.");
|
|
// Check if we need to dispose of our current storage.
|
|
if (this->bucketArray != nullptr) {
|
|
delete[] this->bucketArray;
|
|
}
|
|
// Copy the values from the other bit vector.
|
|
bucketCount = bv.bucketCount;
|
|
bitCount = bv.bitCount;
|
|
bucketArray = new uint64_t[bucketCount];
|
|
std::copy(bv.bucketArray, bv.bucketArray + bucketCount, bucketArray);
|
|
updateSizeChange();
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
* Move assigns the given bit vector to the current bit vector.
|
|
*
|
|
* @param bv The bit vector whose content is moved to the current bit vector.
|
|
* @return A reference to this bit vector after the contents of the given bit vector
|
|
* have been moved into it.
|
|
*/
|
|
BitVector& operator=(BitVector&& bv) {
|
|
LOG4CPLUS_DEBUG(logger, "Performing move assignment.");
|
|
// Only perform the assignment if the source and target are not identical.
|
|
if (this != &bv) {
|
|
// Check if we need to dispose of our current storage.
|
|
if (this->bucketArray != nullptr) {
|
|
delete[] this->bucketArray;
|
|
}
|
|
|
|
// Copy the values from the other bit vector, but directly steal its storage.
|
|
bucketCount = bv.bucketCount;
|
|
bitCount = bv.bitCount;
|
|
bucketArray = bv.bucketArray;
|
|
updateSizeChange();
|
|
|
|
// Now alter the other bit vector such that it does not dispose of our stolen storage.
|
|
bv.bucketArray = nullptr;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
* 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) {
|
|
bitCount = newLength;
|
|
uint_fast64_t newBucketCount = newLength >> 6;
|
|
if ((newLength & mod64mask) != 0) {
|
|
++newBucketCount;
|
|
}
|
|
|
|
// Reserve a temporary array for copying.
|
|
uint64_t* tempArray = new uint64_t[newBucketCount];
|
|
|
|
// Copy over the elements from the old bit vector.
|
|
uint_fast64_t copySize = (newBucketCount <= bucketCount) ? newBucketCount : bucketCount;
|
|
std::copy(this->bucketArray, this->bucketArray + copySize, tempArray);
|
|
|
|
// Initialize missing values in the new bit vector.
|
|
for (uint_fast64_t i = copySize; i < newBucketCount; ++i) {
|
|
tempArray[i] = 0;
|
|
}
|
|
|
|
updateSizeChange();
|
|
|
|
// Dispose of the old bit vector and set the new one.
|
|
delete[] this->bucketArray;
|
|
this->bucketArray = tempArray;
|
|
this->bucketCount = newBucketCount;
|
|
}
|
|
|
|
/*!
|
|
* 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, bool value = true) {
|
|
uint_fast64_t bucket = index >> 6;
|
|
if (bucket >= this->bucketCount) throw storm::exceptions::OutOfRangeException() << "Written index " << index << " out of bounds.";
|
|
uint_fast64_t mask = static_cast<uint_fast64_t>(1) << (index & mod64mask);
|
|
if (value) {
|
|
this->bucketArray[bucket] |= mask;
|
|
} else {
|
|
this->bucketArray[bucket] &= ~mask;
|
|
}
|
|
if (bucket == this->bucketCount - 1) {
|
|
truncateLastBucket();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Sets all bits in the given iterator range.
|
|
*
|
|
* @param begin The begin of the iterator range.
|
|
* @param end The element past the last element of the iterator range.
|
|
*/
|
|
template<typename InputIterator>
|
|
void set(InputIterator begin, InputIterator end) {
|
|
for (InputIterator it = begin; it != end; ++it) {
|
|
this->set(*it);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* 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) const {
|
|
uint_fast64_t bucket = index >> 6;
|
|
if (bucket >= this->bucketCount) throw storm::exceptions::OutOfRangeException() << "Read index " << index << " out of bounds.";
|
|
uint_fast64_t mask = static_cast<uint_fast64_t>(1) << (index & mod64mask);
|
|
return ((this->bucketArray[bucket] & mask) == mask);
|
|
}
|
|
|
|
/*!
|
|
* 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) const {
|
|
uint_fast64_t minSize = (bv.bitCount < this->bitCount) ? bv.bitCount : this->bitCount;
|
|
|
|
// Create resulting bit vector and perform the operation on the individual elements.
|
|
BitVector result(minSize);
|
|
for (uint_fast64_t i = 0; i < result.bucketCount; ++i) {
|
|
result.bucketArray[i] = this->bucketArray[i] & bv.bucketArray[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Performs a logical "and" with the given bit vector and assigns the result to the
|
|
* current 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 reference to the current bit vector corresponding to the logical "and"
|
|
* of the two bit vectors.
|
|
*/
|
|
BitVector& operator&=(BitVector const& bv) {
|
|
uint_fast64_t minSize = (bv.bucketCount < this->bucketCount) ? bv.bucketCount : this->bucketCount;
|
|
|
|
for (uint_fast64_t i = 0; i < minSize; ++i) {
|
|
this->bucketArray[i] &= bv.bucketArray[i];
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
* 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) const {
|
|
uint_fast64_t minSize = (bv.bitCount < this->bitCount) ? bv.bitCount : this->bitCount;
|
|
|
|
// Create resulting bit vector and perform the operation on the individual elements.
|
|
BitVector result(minSize);
|
|
for (uint_fast64_t i = 0; i < result.bucketCount; ++i) {
|
|
result.bucketArray[i] = this->bucketArray[i] | bv.bucketArray[i];
|
|
}
|
|
|
|
result.truncateLastBucket();
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Performs a logical "or" with the given bit vector and assigns the result to the
|
|
* current 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 reference to the current bit vector corresponding to the logical "or"
|
|
* of the two bit vectors.
|
|
*/
|
|
BitVector& operator|=(BitVector const& bv) {
|
|
uint_fast64_t minSize = (bv.bucketCount < this->bucketCount) ? bv.bucketCount : this->bucketCount;
|
|
|
|
for (uint_fast64_t i = 0; i < minSize; ++i) {
|
|
this->bucketArray[i] |= bv.bucketArray[i];
|
|
}
|
|
|
|
truncateLastBucket();
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
* 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) const {
|
|
uint_fast64_t minSize = (bv.bitCount < this->bitCount) ? bv.bitCount : this->bitCount;
|
|
|
|
// Create resulting bit vector and perform the operation on the individual elements.
|
|
BitVector result(minSize);
|
|
for (uint_fast64_t i = 0; i < result.bucketCount; ++i) {
|
|
result.bucketArray[i] = this->bucketArray[i] ^ bv.bucketArray[i];
|
|
}
|
|
|
|
result.truncateLastBucket();
|
|
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~() const {
|
|
// Create resulting bit vector and perform the operation on the individual elements.
|
|
BitVector result(this->bitCount);
|
|
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
|
|
result.bucketArray[i] = ~this->bucketArray[i];
|
|
}
|
|
|
|
result.truncateLastBucket();
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Negates all bits in the bit vector.
|
|
*/
|
|
void complement() {
|
|
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
|
|
this->bucketArray[i] = ~this->bucketArray[i];
|
|
}
|
|
truncateLastBucket();
|
|
}
|
|
|
|
/*!
|
|
* 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 "implies" of the two bit vectors.
|
|
*/
|
|
BitVector implies(BitVector const& bv) const {
|
|
uint_fast64_t minSize = (bv.bitCount < this->bitCount) ? bv.bitCount : this->bitCount;
|
|
|
|
// Create resulting bit vector and perform the operation on the individual elements.
|
|
BitVector result(minSize);
|
|
for (uint_fast64_t i = 0; i < result.bucketCount - 1; ++i) {
|
|
result.bucketArray[i] = ~this->bucketArray[i] | bv.bucketArray[i];
|
|
}
|
|
|
|
result.truncateLastBucket();
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Checks whether all bits that are set in the current bit vector are also set in the given bit
|
|
* vector.
|
|
*
|
|
* @param bv A reference to the bit vector whose bits are (possibly) a superset of the bits of
|
|
* the current bit vector.
|
|
* @return True iff all bits that are set in the current bit vector are also set in the given bit
|
|
* vector.
|
|
*/
|
|
bool isSubsetOf(BitVector const& bv) const {
|
|
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
|
|
if ((this->bucketArray[i] & bv.bucketArray[i]) != this->bucketArray[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* Checks whether none of the bits that are set in the current bit vector are also set in the
|
|
* given bit vector.
|
|
*
|
|
* @param bv A reference to the bit vector whose bits are (possibly) disjoint from the bits in
|
|
* the current bit vector.
|
|
* @returns True iff none of the bits that are set in the current bit vector are also set in the
|
|
* given bit vector.
|
|
*/
|
|
bool isDisjointFrom(BitVector const& bv) const {
|
|
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
|
|
if ((this->bucketArray[i] & bv.bucketArray[i]) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* Computes a bit vector such that bit i is set iff the i-th set bit of the current bit vector is also contained
|
|
* in the given bit vector.
|
|
*
|
|
* @param bv A reference the bit vector to be used.
|
|
* @return A bit vector whose i-th bit is set iff the i-th set bit of the current bit vector is also contained
|
|
* in the given bit vector.
|
|
*/
|
|
BitVector operator%(BitVector const& bv) const {
|
|
// Create resulting bit vector.
|
|
BitVector result(this->getNumberOfSetBits());
|
|
|
|
// If the current bit vector has not too many elements compared to the given bit vector we prefer iterating
|
|
// over its elements.
|
|
if (this->getNumberOfSetBits() / 10 < bv.getNumberOfSetBits()) {
|
|
uint_fast64_t position = 0;
|
|
for (auto bit : *this) {
|
|
if (bv.get(bit)) {
|
|
result.set(position, true);
|
|
}
|
|
++position;
|
|
}
|
|
} else {
|
|
// If the given bit vector had much less elements, we iterate over its elements and accept calling the more
|
|
// costly operation getNumberOfSetBitsBeforeIndex on the current bit vector.
|
|
for (auto bit : bv) {
|
|
if (this->get(bit)) {
|
|
result.set(this->getNumberOfSetBitsBeforeIndex(bit), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Retrieves whether there is at least one bit set in the vector.
|
|
*
|
|
* @return True if there is at least one bit set in this vector.
|
|
*/
|
|
bool empty() const {
|
|
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
|
|
if (this->bucketArray[i] != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* Removes all set bits from the bit vector.
|
|
*/
|
|
void clear() {
|
|
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
|
|
this->bucketArray[i] = 0;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Returns a list containing all indices such that the bits at these indices are set to true
|
|
* in the bit vector.
|
|
*
|
|
* @return A vector of indices of set bits in the bit vector.
|
|
*/
|
|
std::vector<uint_fast64_t> getSetIndicesList() const {
|
|
std::vector<uint_fast64_t> result;
|
|
result.reserve(this->getNumberOfSetBits());
|
|
for (auto index : *this) {
|
|
result.push_back(index);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Adds all indices of bits set to one to the given list.
|
|
*
|
|
* @param list The list to which to append the indices.
|
|
*/
|
|
void addSetIndicesToVector(std::vector<uint_fast64_t>& vector) const {
|
|
for (auto index : *this) {
|
|
vector.push_back(index);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Returns the number of bits that are set (to one) in this bit vector.
|
|
* @return The number of bits that are set (to one) in this bit vector.
|
|
*/
|
|
uint_fast64_t getNumberOfSetBits() const {
|
|
return getNumberOfSetBitsBeforeIndex(bucketCount << 6);
|
|
}
|
|
|
|
uint_fast64_t getNumberOfSetBitsBeforeIndex(uint_fast64_t index) const {
|
|
uint_fast64_t result = 0;
|
|
// First, count all full buckets.
|
|
uint_fast64_t bucket = index >> 6;
|
|
for (uint_fast64_t i = 0; i < bucket; ++i) {
|
|
// Check if we are using g++ or clang++ and, if so, use the built-in function
|
|
#if (defined (__GNUG__) || defined(__clang__))
|
|
result += __builtin_popcountll(this->bucketArray[i]);
|
|
#elif defined WINDOWS
|
|
#include <nmmintrin.h>
|
|
// if the target machine does not support SSE4, this will fail.
|
|
result += _mm_popcnt_u64(this->bucketArray[i]);
|
|
#else
|
|
uint_fast32_t cnt;
|
|
uint_fast64_t bitset = this->bucketArray[i];
|
|
for (cnt = 0; bitset; cnt++) {
|
|
bitset &= bitset - 1;
|
|
}
|
|
result += cnt;
|
|
#endif
|
|
}
|
|
|
|
// Now check if we have to count part of a bucket.
|
|
uint64_t tmp = index & mod64mask;
|
|
if (tmp != 0) {
|
|
tmp = ((1ll << (tmp & mod64mask)) - 1ll);
|
|
tmp &= bucketArray[bucket];
|
|
// Check if we are using g++ or clang++ and, if so, use the built-in function
|
|
#if (defined (__GNUG__) || defined(__clang__))
|
|
result += __builtin_popcountll(tmp);
|
|
#else
|
|
uint_fast32_t cnt;
|
|
uint64_t bitset = tmp;
|
|
for (cnt = 0; bitset; cnt++) {
|
|
bitset &= bitset - 1;
|
|
}
|
|
result += cnt;
|
|
#endif
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Retrieves the number of bits this bit vector can store.
|
|
*/
|
|
uint_fast64_t getSize() const {
|
|
return bitCount;
|
|
}
|
|
|
|
/*!
|
|
* Returns the size of the bit vector in memory measured in bytes.
|
|
* @return The size of the bit vector in memory measured in bytes.
|
|
*/
|
|
uint_fast64_t getSizeInMemory() const {
|
|
return sizeof(*this) + sizeof(uint_fast64_t) * bucketCount;
|
|
}
|
|
|
|
/*!
|
|
* Returns an iterator to the indices of the set bits in the bit vector.
|
|
*/
|
|
constIndexIterator begin() const {
|
|
return constIndexIterator(*this, 0, bitCount);
|
|
}
|
|
|
|
/*!
|
|
* Returns an iterator pointing at the element past the bit vector.
|
|
*/
|
|
const constIndexIterator& end() const {
|
|
return endIterator;
|
|
}
|
|
|
|
/*!
|
|
* Returns a string representation of the bit vector.
|
|
*/
|
|
std::string toString() const {
|
|
std::stringstream result;
|
|
result << "bit vector(" << this->getNumberOfSetBits() << "/" << bitCount << ") [";
|
|
for (auto index : *this) {
|
|
result << index << " ";
|
|
}
|
|
result << "]";
|
|
|
|
return result.str();
|
|
}
|
|
|
|
/*!
|
|
* Calculates a hash over all values contained in this Sparse Matrix.
|
|
* @return size_t A Hash Value
|
|
*/
|
|
std::size_t getHash() const {
|
|
std::size_t result = 0;
|
|
|
|
boost::hash_combine(result, bucketCount);
|
|
boost::hash_combine(result, bitCount);
|
|
|
|
for (uint_fast64_t i = 0; i < bucketCount; ++i) {
|
|
boost::hash_combine(result, bucketArray[i]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
|
|
/*!
|
|
* Retrieves the index of the bit that is set after the given starting index,
|
|
* but before the given end index in the given bit vector.
|
|
* @param bitVector The bit vector to search.
|
|
* @param startingIndex The index where to start the search.
|
|
* @param endIndex The end index at which to stop the search.
|
|
* @return The index of the bit that is set after the given starting index,
|
|
* but before the given end index in the given bit vector or endIndex in case
|
|
* the end index was reached.
|
|
*/
|
|
uint_fast64_t getNextSetIndex(uint_fast64_t startingIndex, uint_fast64_t endIndex) const {
|
|
uint_fast8_t currentBitInByte = startingIndex & mod64mask;
|
|
startingIndex >>= 6;
|
|
uint64_t* bucketPtr = this->bucketArray + startingIndex;
|
|
|
|
while ((startingIndex << 6) < endIndex) {
|
|
// Compute the remaining bucket content by a right shift
|
|
// to the current bit.
|
|
uint_fast64_t remainingInBucket = *bucketPtr >> currentBitInByte;
|
|
// Check if there is at least one bit in the remainder of the bucket
|
|
// that is set to true.
|
|
if (remainingInBucket != 0) {
|
|
// Find that bit.
|
|
while ((remainingInBucket & 1) == 0) {
|
|
remainingInBucket >>= 1;
|
|
++currentBitInByte;
|
|
}
|
|
// Only return the index of the set bit if we are still in the
|
|
// valid range.
|
|
if ((startingIndex << 6) + currentBitInByte < endIndex) {
|
|
return (startingIndex << 6) + currentBitInByte;
|
|
} else {
|
|
return endIndex;
|
|
}
|
|
}
|
|
|
|
// Advance to the next bucket.
|
|
++startingIndex; ++bucketPtr; currentBitInByte = 0;
|
|
}
|
|
return endIndex;
|
|
}
|
|
|
|
/*!
|
|
* Truncate the last bucket so that no bits are set in the range starting
|
|
* from (bitCount + 1).
|
|
*/
|
|
void truncateLastBucket() {
|
|
if ((bitCount & mod64mask) != 0) {
|
|
bucketArray[bucketCount - 1] = bucketArray[bucketCount - 1] & truncateMask;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Updates internal structures in case the size of the bit vector changed. Needs to be called
|
|
* after the size of the bit vector changed.
|
|
*/
|
|
void updateSizeChange() {
|
|
endIterator.currentIndex = bitCount;
|
|
truncateMask = (1ll << (bitCount & mod64mask)) - 1ll;
|
|
}
|
|
|
|
/*! The number of 64-bit buckets we use as internal storage. */
|
|
uint_fast64_t bucketCount;
|
|
|
|
/*! The number of bits that have to be stored */
|
|
uint_fast64_t bitCount;
|
|
|
|
/*! Array of 64-bit buckets to store the bits. */
|
|
uint64_t* bucketArray;
|
|
|
|
/*! An iterator marking the end of the bit vector. */
|
|
constIndexIterator endIterator;
|
|
|
|
/*! 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;
|
|
|
|
uint64_t truncateMask;
|
|
};
|
|
|
|
} // namespace storage
|
|
|
|
} // namespace storm
|
|
|
|
#endif // STORM_STORAGE_BITVECTOR_H_
|