// Copyright 2010-2018 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Various utility functions on bitsets. #ifndef OR_TOOLS_UTIL_BITSET_H_ #define OR_TOOLS_UTIL_BITSET_H_ #include #include #include #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" namespace operations_research { // Basic bit operations // Useful constants: word and double word will all bits set. static const uint64 kAllBits64 = GG_ULONGLONG(0xFFFFFFFFFFFFFFFF); static const uint64 kAllBitsButLsb64 = GG_ULONGLONG(0xFFFFFFFFFFFFFFFE); static const uint32 kAllBits32 = 0xFFFFFFFFU; // Returns a word with only bit pos set. inline uint64 OneBit64(int pos) { return GG_ULONGLONG(1) << pos; } inline uint32 OneBit32(int pos) { return 1U << pos; } // Returns the number of bits set in n. inline uint64 BitCount64(uint64 n) { const uint64 m1 = GG_ULONGLONG(0x5555555555555555); const uint64 m2 = GG_ULONGLONG(0x3333333333333333); const uint64 m4 = GG_ULONGLONG(0x0F0F0F0F0F0F0F0F); const uint64 h01 = GG_ULONGLONG(0x0101010101010101); n -= (n >> 1) & m1; n = (n & m2) + ((n >> 2) & m2); n = (n + (n >> 4)) & m4; n = (n * h01) >> 56; return n; } inline uint32 BitCount32(uint32 n) { n -= (n >> 1) & 0x55555555UL; n = (n & 0x33333333) + ((n >> 2) & 0x33333333UL); n = (n + (n >> 4)) & 0x0F0F0F0FUL; n = n + (n >> 8); n = n + (n >> 16); return n & 0x0000003FUL; } // Returns a word with only the least significant bit of n set. inline uint64 LeastSignificantBitWord64(uint64 n) { return n & ~(n - 1); } inline uint32 LeastSignificantBitWord32(uint32 n) { return n & ~(n - 1); } // Returns the least significant bit position in n. // Discussion around lsb computation: // De Bruijn is almost as fast as the bsr/bsf-instruction-based intrinsics. // Both are always much faster than the Default algorithm. #define USE_DEBRUIJN true // if true, use de Bruijn bit forward scanner. #if defined(__GNUC__) || defined(__llvm__) #define USE_FAST_LEAST_SIGNIFICANT_BIT true // if true, use fast lsb. #endif #if defined(USE_FAST_LEAST_SIGNIFICANT_BIT) inline int LeastSignificantBitPosition64Fast(uint64 n) { return n == 0 ? 0 : __builtin_ctzll(n); } #endif inline int LeastSignificantBitPosition64DeBruijn(uint64 n) { static const uint64 kSeq = GG_ULONGLONG(0x0218a392dd5fb34f); static const int kTab[64] = { // initialized by 'kTab[(kSeq << i) >> 58] = i 0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 52, 20, 58, 5, 17, 26, 56, 15, 38, 29, 40, 10, 49, 53, 31, 21, 34, 59, 42, 63, 6, 12, 18, 24, 27, 51, 57, 16, 55, 37, 39, 48, 30, 33, 41, 62, 11, 23, 50, 54, 36, 47, 32, 61, 22, 35, 46, 60, 45, 44, 43, }; return kTab[((n & (~n + 1)) * kSeq) >> 58]; } inline int LeastSignificantBitPosition64Default(uint64 n) { if (n == 0) return 0; int pos = 63; if (n & 0x00000000FFFFFFFFLL) { pos -= 32; } else { n >>= 32; } if (n & 0x000000000000FFFFLL) { pos -= 16; } else { n >>= 16; } if (n & 0x00000000000000FFLL) { pos -= 8; } else { n >>= 8; } if (n & 0x000000000000000FLL) { pos -= 4; } else { n >>= 4; } if (n & 0x0000000000000003LL) { pos -= 2; } else { n >>= 2; } if (n & 0x0000000000000001LL) { pos -= 1; } return pos; } inline int LeastSignificantBitPosition64(uint64 n) { DCHECK_NE(n, 0); #ifdef USE_FAST_LEAST_SIGNIFICANT_BIT return LeastSignificantBitPosition64Fast(n); #elif defined(USE_DEBRUIJN) return LeastSignificantBitPosition64DeBruijn(n); #else return LeastSignificantBitPosition64Default(n); #endif } #if defined(USE_FAST_LEAST_SIGNIFICANT_BIT) inline int LeastSignificantBitPosition32Fast(uint32 n) { return n == 0 ? 0 : __builtin_ctzl(n); } #endif inline int LeastSignificantBitPosition32DeBruijn(uint32 n) { static const uint32 kSeq = 0x077CB531U; // de Bruijn sequence static const int kTab[32] = {// initialized by 'kTab[(kSeq << i) >> 27] = i 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; return kTab[((n & (~n + 1)) * kSeq) >> 27]; } inline int LeastSignificantBitPosition32Default(uint32 n) { if (n == 0) return 0; int pos = 31; if (n & 0x0000FFFFL) { pos -= 16; } else { n >>= 16; } if (n & 0x000000FFL) { pos -= 8; } else { n >>= 8; } if (n & 0x0000000FL) { pos -= 4; } else { n >>= 4; } if (n & 0x00000003L) { pos -= 2; } else { n >>= 2; } if (n & 0x00000001L) { pos -= 1; } return pos; } inline int LeastSignificantBitPosition32(uint32 n) { DCHECK_NE(n, 0); #ifdef USE_FAST_LEAST_SIGNIFICANT_BIT return LeastSignificantBitPosition32Fast(n); #elif defined(USE_DEBRUIJN) return LeastSignificantBitPosition32DeBruijn(n); #else return LeastSignificantBitPosition32Default(n); #endif } // Returns the most significant bit position in n. #if USE_FAST_LEAST_SIGNIFICANT_BIT inline int MostSignificantBitPosition64Fast(uint64 n) { // __builtin_clzll(1) should always return 63. There is no penalty in // using offset, and the code looks more like its uint32 counterpart. const int offset = __builtin_clzll(1); return n == 0 ? 0 : (offset - __builtin_clzll(n)); } #endif inline int MostSignificantBitPosition64Default(uint64 n) { int b = 0; if (0 != (n & (kAllBits64 << (1 << 5)))) { b |= (1 << 5); n >>= (1 << 5); } if (0 != (n & (kAllBits64 << (1 << 4)))) { b |= (1 << 4); n >>= (1 << 4); } if (0 != (n & (kAllBits64 << (1 << 3)))) { b |= (1 << 3); n >>= (1 << 3); } if (0 != (n & (kAllBits64 << (1 << 2)))) { b |= (1 << 2); n >>= (1 << 2); } if (0 != (n & (kAllBits64 << (1 << 1)))) { b |= (1 << 1); n >>= (1 << 1); } if (0 != (n & (kAllBits64 << (1 << 0)))) { b |= (1 << 0); } return b; } inline int MostSignificantBitPosition64(uint64 n) { #ifdef USE_FAST_LEAST_SIGNIFICANT_BIT return MostSignificantBitPosition64Fast(n); #else return MostSignificantBitPosition64Default(n); #endif } #if USE_FAST_LEAST_SIGNIFICANT_BIT inline int MostSignificantBitPosition32Fast(uint32 n) { // The constant here depends on whether we are on a 32-bit or 64-bit machine. // __builtin_clzl(1) returns 63 on a 64-bit machine and 31 on a 32-bit // machine. const int offset = __builtin_clzl(1); return n == 0 ? 0 : (offset - __builtin_clzl(n)); } #endif inline int MostSignificantBitPosition32Default(uint32 n) { int b = 0; if (0 != (n & (kAllBits32 << (1 << 4)))) { b |= (1 << 4); n >>= (1 << 4); } if (0 != (n & (kAllBits32 << (1 << 3)))) { b |= (1 << 3); n >>= (1 << 3); } if (0 != (n & (kAllBits32 << (1 << 2)))) { b |= (1 << 2); n >>= (1 << 2); } if (0 != (n & (kAllBits32 << (1 << 1)))) { b |= (1 << 1); n >>= (1 << 1); } if (0 != (n & (kAllBits32 << (1 << 0)))) { b |= (1 << 0); } return b; } inline int MostSignificantBitPosition32(uint32 n) { #ifdef USE_FAST_LEAST_SIGNIFICANT_BIT return MostSignificantBitPosition32Fast(n); #else return MostSignificantBitPosition32Default(n); #endif } #undef USE_DEBRUIJN #undef USE_FAST_LEAST_SIGNIFICANT_BIT // Returns a word with bits from s to e set. inline uint64 OneRange64(uint64 s, uint64 e) { DCHECK_LE(s, 63); DCHECK_LE(e, 63); DCHECK_LE(s, e); return (kAllBits64 << s) ^ ((kAllBits64 - 1) << e); } inline uint32 OneRange32(uint32 s, uint32 e) { DCHECK_LE(s, 31); DCHECK_LE(e, 31); DCHECK_LE(s, e); return (kAllBits32 << s) ^ ((kAllBits32 - 1) << e); } // Returns a word with s least significant bits unset. inline uint64 IntervalUp64(uint64 s) { DCHECK_LE(s, 63); return kAllBits64 << s; } inline uint32 IntervalUp32(uint32 s) { DCHECK_LE(s, 31); return kAllBits32 << s; } // Returns a word with the s most significant bits unset. inline uint64 IntervalDown64(uint64 s) { DCHECK_LE(s, 63); return kAllBits64 >> (63 - s); } inline uint32 IntervalDown32(uint32 s) { DCHECK_LE(s, 31); return kAllBits32 >> (31 - s); } // ----- Bitset operators ----- // Bitset: array of uint32/uint64 words // Bit operators used to manipulates bitsets. // Returns the bit number in the word computed by BitOffsetXX, // corresponding to the bit at position pos in the bitset. // Note: '& 63' is faster than '% 64' // TODO(user): rename BitPos and BitOffset to something more understandable. inline uint32 BitPos64(uint64 pos) { return (pos & 63); } inline uint32 BitPos32(uint32 pos) { return (pos & 31); } // Returns the word number corresponding to bit number pos. inline uint64 BitOffset64(uint64 pos) { return (pos >> 6); } inline uint32 BitOffset32(uint32 pos) { return (pos >> 5); } // Returns the number of words needed to store size bits. inline uint64 BitLength64(uint64 size) { return ((size + 63) >> 6); } inline uint32 BitLength32(uint32 size) { return ((size + 31) >> 5); } // Returns the bit number in the bitset of the first bit of word number v. inline uint64 BitShift64(uint64 v) { return v << 6; } inline uint32 BitShift32(uint32 v) { return v << 5; } // Returns true if the bit pos is set in bitset. inline bool IsBitSet64(const uint64* const bitset, uint64 pos) { return (bitset[BitOffset64(pos)] & OneBit64(BitPos64(pos))); } inline bool IsBitSet32(const uint32* const bitset, uint32 pos) { return (bitset[BitOffset32(pos)] & OneBit32(BitPos32(pos))); } // Sets the bit pos to true in bitset. inline void SetBit64(uint64* const bitset, uint64 pos) { bitset[BitOffset64(pos)] |= OneBit64(BitPos64(pos)); } inline void SetBit32(uint32* const bitset, uint32 pos) { bitset[BitOffset32(pos)] |= OneBit32(BitPos32(pos)); } // Sets the bit pos to false in bitset. inline void ClearBit64(uint64* const bitset, uint64 pos) { bitset[BitOffset64(pos)] &= ~OneBit64(BitPos64(pos)); } inline void ClearBit32(uint32* const bitset, uint32 pos) { bitset[BitOffset32(pos)] &= ~OneBit32(BitPos32(pos)); } // Returns the number of bits set in bitset between positions start and end. uint64 BitCountRange64(const uint64* const bitset, uint64 start, uint64 end); uint32 BitCountRange32(const uint32* const bitset, uint32 start, uint32 end); // Returns true if no bits are set in bitset between start and end. bool IsEmptyRange64(const uint64* const bitset, uint64 start, uint64 end); bool IsEmptyRange32(const uint32* const bitset, uint32 start, uint32 end); // Returns the first bit set in bitset between start and max_bit. int64 LeastSignificantBitPosition64(const uint64* const bitset, uint64 start, uint64 end); int LeastSignificantBitPosition32(const uint32* const bitset, uint32 start, uint32 end); // Returns the last bit set in bitset between min_bit and start. int64 MostSignificantBitPosition64(const uint64* const bitset, uint64 start, uint64 end); int MostSignificantBitPosition32(const uint32* const bitset, uint32 start, uint32 end); // Unsafe versions of the functions above where respectively end and start // are supposed to be set. int64 UnsafeLeastSignificantBitPosition64(const uint64* const bitset, uint64 start, uint64 end); int32 UnsafeLeastSignificantBitPosition32(const uint32* const bitset, uint32 start, uint32 end); int64 UnsafeMostSignificantBitPosition64(const uint64* const bitset, uint64 start, uint64 end); int32 UnsafeMostSignificantBitPosition32(const uint32* const bitset, uint32 start, uint32 end); // Returns a mask with the bits pos % 64 and (pos ^ 1) % 64 sets. inline uint64 TwoBitsFromPos64(uint64 pos) { return GG_ULONGLONG(3) << (pos & 62); } // This class is like an ITIVector except that it provides a // more efficient way to iterate over the positions set to true. It achieves // this by caching the current uint64 bucket in the Iterator and using // LeastSignificantBitPosition64() to iterate over the positions at 1 in this // bucket. template class Bitset64 { public: Bitset64() : size_(), data_(), end_(*this, /*at_end=*/true) {} explicit Bitset64(IndexType size) : size_(Value(size) > 0 ? size : IndexType(0)), data_(BitLength64(Value(size_))), end_(*this, /*at_end=*/true) {} // Returns how many bits this Bitset64 can hold. IndexType size() const { return size_; } // Appends value at the end of the bitset. void PushBack(bool value) { ++size_; data_.resize(BitLength64(Value(size_)), 0); Set(size_ - 1, value); } // Resizes the Bitset64 to the given number of bits. New bits are sets to 0. void Resize(IndexType size) { DCHECK_GE(Value(size), 0); size_ = Value(size) > 0 ? size : IndexType(0); data_.resize(BitLength64(Value(size_)), 0); } // Changes the number of bits the Bitset64 can hold and set all of them to 0. void ClearAndResize(IndexType size) { DCHECK_GE(Value(size), 0); size_ = Value(size) > 0 ? size : IndexType(0); // Memset is 4x faster than data_.assign() as of 19/03/2014. // TODO(user): Ideally if a realloc happens, we don't need to copy the old // data... const size_t bit_length = static_cast(BitLength64(Value(size_))); const size_t to_clear = std::min(data_.size(), bit_length); data_.resize(bit_length, 0); memset(data_.data(), 0, to_clear * sizeof(int64)); } // Sets all bits to 0. void ClearAll() { memset(data_.data(), 0, data_.size() * sizeof(int64)); } // Sets the bit at position i to 0. void Clear(IndexType i) { DCHECK_GE(Value(i), 0); DCHECK_LT(Value(i), Value(size_)); data_[BitOffset64(Value(i))] &= ~OneBit64(BitPos64(Value(i))); } // Sets bucket containing bit i to 0. void ClearBucket(IndexType i) { DCHECK_GE(Value(i), 0); DCHECK_LT(Value(i), Value(size_)); data_[BitOffset64(Value(i))] = 0; } // Clears the bits at position i and i ^ 1. void ClearTwoBits(IndexType i) { DCHECK_GE(Value(i), 0); DCHECK_LT(Value(i), Value(size_)); data_[BitOffset64(Value(i))] &= ~TwoBitsFromPos64(Value(i)); } // Returns true if the bit at position i or the one at position i ^ 1 is set. bool AreOneOfTwoBitsSet(IndexType i) const { DCHECK_GE(Value(i), 0); DCHECK_LT(Value(i), Value(size_)); return data_[BitOffset64(Value(i))] & TwoBitsFromPos64(Value(i)); } // Returns true if the bit at position i is set. bool IsSet(IndexType i) const { DCHECK_GE(Value(i), 0); DCHECK_LT(Value(i), Value(size_)); return data_[BitOffset64(Value(i))] & OneBit64(BitPos64(Value(i))); } // Same as IsSet(). bool operator[](IndexType i) const { return IsSet(i); } // Sets the bit at position i to 1. void Set(IndexType i) { DCHECK_GE(Value(i), 0); DCHECK_LT(Value(i), size_); data_[BitOffset64(Value(i))] |= OneBit64(BitPos64(Value(i))); } // If value is true, sets the bit at position i to 1, sets it to 0 otherwise. void Set(IndexType i, bool value) { if (value) { Set(i); } else { Clear(i); } } // Copies bucket containing bit i from "other" to "this". void CopyBucket(const Bitset64& other, IndexType i) { const uint64 offset = BitOffset64(Value(i)); data_[offset] = other.data_[offset]; } // Copies "other" to "this". The bitsets do not have to be of the same size. // If "other" is smaller, high order bits are not changed. If "other" is // larger, its high order bits are ignored. In any case "this" is not resized. template void SetContentFromBitset(const Bitset64& other) { const int64 min_size = std::min(data_.size(), other.data_.size()); if (min_size == 0) return; const uint64 last_common_bucket = data_[min_size - 1]; memcpy(data_.data(), other.data_.data(), min_size * sizeof(uint64)); if (data_.size() >= other.data_.size()) { const uint64 bitmask = kAllBitsButLsb64 << BitPos64(other.Value(other.size() - 1)); data_[min_size - 1] &= ~bitmask; data_[min_size - 1] |= (bitmask & last_common_bucket); } } // Same as SetContentFromBitset where "this" and "other" have the same size. template void SetContentFromBitsetOfSameSize(const Bitset64& other) { DCHECK_EQ(Value(size()), other.Value(other.size())); memcpy(data_.data(), other.data_.data(), data_.size() * sizeof(uint64)); } // Sets "this" to be the intersection of "this" and "other". The // bitsets do not have to be the same size. If other is smaller, all // the higher order bits are assumed to be 0. void Intersection(const Bitset64& other) { const int min_size = std::min(data_.size(), other.data_.size()); for (int i = 0; i < min_size; ++i) { data_[i] &= other.data_[i]; } for (int i = min_size; i < data_.size(); ++i) { data_[i] = 0; } } // Sets "this" to be the union of "this" and "other". The // bitsets do not have to be the same size. If other is smaller, all // the higher order bits are assumed to be 0. void Union(const Bitset64& other) { const int min_size = std::min(data_.size(), other.data_.size()); for (int i = 0; i < min_size; ++i) { data_[i] |= other.data_[i]; } } // Class to iterate over the bit positions at 1 of a Bitset64. // // IMPORTANT: Because the iterator "caches" the current uint64 bucket, this // will probably not do what you want if Bitset64 is modified while iterating. class Iterator { public: explicit Iterator(const Bitset64& data_) : bitset_(data_), index_(0), base_index_(0), current_(0) { if (bitset_.data_.empty()) { index_ = -1; } else { current_ = bitset_.data_[0]; Next(); } } // Returns true if the Iterator is at a valid position. bool Ok() const { return index_ != -1; } // Returns the current position of the iterator. IndexType Index() const { DCHECK(Ok()); return IndexType(index_); } // Moves the iterator the the next position at 1 of the Bitset64. void Next() { DCHECK(Ok()); if (current_ == 0) { int bucket = BitOffset64(base_index_); const int size = bitset_.data_.size(); do { bucket++; } while (bucket < size && bitset_.data_[bucket] == 0); if (bucket == size) { index_ = -1; return; } current_ = bitset_.data_[bucket]; base_index_ = BitShift64(bucket); } // Computes the index and clear the least significant bit of current_. index_ = base_index_ + LeastSignificantBitPosition64(current_); current_ &= current_ - 1; } // STL version of the functions above to support range-based "for" loop. Iterator(const Bitset64& data_, bool at_end) : bitset_(data_), index_(0), base_index_(0), current_(0) { if (at_end || bitset_.data_.empty()) { index_ = -1; } else { current_ = bitset_.data_[0]; Next(); } } bool operator!=(const Iterator& other) const { return index_ != other.index_; } IndexType operator*() const { return IndexType(index_); } void operator++() { Next(); } private: const Bitset64& bitset_; int index_; int base_index_; uint64 current_; }; // Allows range-based "for" loop on the non-zero positions: // for (const IndexType index : bitset) {} // instead of: // for (Bitset64::Iterator it(bitset); it.Ok(); it.Next()) { // const IndexType index = it.Index(); Iterator begin() const { return Iterator(*this); } Iterator end() const { return end_; } // Cryptic function! // This is just an optimized version of a given piece of code and has probably // little general use. Sets the bit at position i to the result of // (other1[i] && use1) XOR (other2[i] && use2). void SetBitFromOtherBitSets(IndexType i, const Bitset64& other1, uint64 use1, const Bitset64& other2, uint64 use2) { DCHECK_EQ(data_.size(), other1.data_.size()); DCHECK_EQ(data_.size(), other2.data_.size()); DCHECK(use1 == 0 || use1 == 1); DCHECK(use2 == 0 || use2 == 1); const int bucket = BitOffset64(Value(i)); const int pos = BitPos64(Value(i)); data_[bucket] ^= ((1ull << pos) & data_[bucket]) ^ ((use1 << pos) & other1.data_[bucket]) ^ ((use2 << pos) & other2.data_[bucket]); } // Returns a 0/1 string representing the bitset. std::string DebugString() const { std::string output; for (IndexType i(0); i < size(); ++i) { output += IsSet(i) ? "1" : "0"; } return output; } private: // Returns the value of the index type. // This function is specialized below to work with IntType and int64. int64 Value(IndexType input) const; IndexType size_; std::vector data_; // It is faster to store the end() Iterator than to recompute it every time. // Note that we cannot do the same for begin(). const Iterator end_; template friend class Bitset64; DISALLOW_COPY_AND_ASSIGN(Bitset64); }; // Specialized version of Bitset64 that allows to query the last bit set more // efficiently. class BitQueue64 { public: BitQueue64() : size_(), top_(-1), data_() {} explicit BitQueue64(int size) : size_(size), top_(-1), data_(BitLength64(size), 0) {} void IncreaseSize(int size) { CHECK_GE(size, size_); size_ = size; data_.resize(BitLength64(size), 0); } void ClearAndResize(int size) { top_ = -1; size_ = size; data_.assign(BitLength64(size), 0); } void Set(int i) { DCHECK_GE(i, 0); DCHECK_LT(i, size_); top_ = std::max(top_, i); data_[BitOffset64(i)] |= OneBit64(BitPos64(i)); } // Sets all the bits from 0 up to i-1 to 1. void SetAllBefore(int i) { DCHECK_GE(i, 0); DCHECK_LT(i, size_); top_ = std::max(top_, i - 1); int bucket_index = static_cast(BitOffset64(i)); data_[bucket_index] |= OneBit64(BitPos64(i)) - 1; for (--bucket_index; bucket_index >= 0; --bucket_index) { data_[bucket_index] = kAllBits64; } } // Returns the position of the highest bit set in O(1) or -1 if no bit is set. int Top() const { return top_; } // Clears the Top() bit and recomputes the position of the next Top(). void ClearTop() { DCHECK_NE(top_, -1); int bucket_index = static_cast(BitOffset64(top_)); uint64 bucket = data_[bucket_index] &= ~OneBit64(BitPos64(top_)); while (!bucket) { if (bucket_index == 0) { top_ = -1; return; } bucket = data_[--bucket_index]; } // Note(user): I experimented with reversing the bit order in a bucket to // use LeastSignificantBitPosition64() and it is only slightly faster at the // cost of a lower Set() speed. So I prefered this version. top_ = static_cast(BitShift64(bucket_index) + MostSignificantBitPosition64(bucket)); } private: int size_; int top_; std::vector data_; DISALLOW_COPY_AND_ASSIGN(BitQueue64); }; // The specialization of Value() for IntType and int64. template inline int64 Bitset64::Value(IntType input) const { DCHECK_GE(input.value(), 0); return input.value(); } template <> inline int64 Bitset64::Value(int64 input) const { DCHECK_GE(input, 0); return input; } // A simple utility class to set/unset integer in a range [0, size). // This is optimized for sparsity. template class SparseBitset { public: SparseBitset() {} explicit SparseBitset(IntegerType size) : bitset_(size) {} IntegerType size() const { return bitset_.size(); } void SparseClearAll() { for (const IntegerType i : to_clear_) bitset_.ClearBucket(i); to_clear_.clear(); } void ClearAll() { bitset_.ClearAll(); to_clear_.clear(); } void ClearAndResize(IntegerType size) { // As of 19/03/2014, experiments show that this is a reasonable threshold. const int kSparseThreshold = 300; if (to_clear_.size() * kSparseThreshold < size) { SparseClearAll(); bitset_.Resize(size); } else { bitset_.ClearAndResize(size); to_clear_.clear(); } } void Resize(IntegerType size) { if (size < bitset_.size()) { int new_index = 0; for (IntegerType index : to_clear_) { if (index < size) { to_clear_[new_index] = index; ++new_index; } } to_clear_.resize(new_index); } bitset_.Resize(size); } bool operator[](IntegerType index) const { return bitset_[index]; } void Set(IntegerType index) { if (!bitset_[index]) { bitset_.Set(index); to_clear_.push_back(index); } } void Clear(IntegerType index) { bitset_.Clear(index); } int NumberOfSetCallsWithDifferentArguments() const { return to_clear_.size(); } const std::vector& PositionsSetAtLeastOnce() const { return to_clear_; } // Tells the class that all its bits are cleared, so it can reset to_clear_ // to the empty vector. Note that this call is "unsafe" since the fact that // the class is actually all cleared is only checked in debug mode. // // This is useful to iterate on the "set" positions while clearing them for // instance. This way, after the loop, a client can call this for efficiency. void NotifyAllClear() { if (DEBUG_MODE) { for (IntegerType index : to_clear_) CHECK(!bitset_[index]); } to_clear_.clear(); } private: Bitset64 bitset_; std::vector to_clear_; DISALLOW_COPY_AND_ASSIGN(SparseBitset); }; } // namespace operations_research #endif // OR_TOOLS_UTIL_BITSET_H_