Files
ortools-clone/ortools/util/bitset.h
2020-10-21 00:21:54 +02:00

840 lines
26 KiB
C++

// 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 <string.h>
#include <algorithm>
#include <vector>
#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<IndexType, bool> 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 <typename IndexType = int64> 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<size_t>(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<IndexType> &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 <typename OtherIndexType>
void SetContentFromBitset(const Bitset64<OtherIndexType> &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 <typename OtherIndexType>
void SetContentFromBitsetOfSameSize(const Bitset64<OtherIndexType> &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<IndexType> &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<IndexType> &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<IndexType>::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<IndexType> &other1,
uint64 use1, const Bitset64<IndexType> &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<uint64> 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 <class OtherIndexType> 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<int>(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<int>(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<int>(BitShift64(bucket_index) +
MostSignificantBitPosition64(bucket));
}
private:
int size_;
int top_;
std::vector<uint64> data_;
DISALLOW_COPY_AND_ASSIGN(BitQueue64);
};
// The specialization of Value() for IntType and int64.
template <typename IntType>
inline int64 Bitset64<IntType>::Value(IntType input) const {
DCHECK_GE(input.value(), 0);
return input.value();
}
template <> inline int64 Bitset64<int64>::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 <typename IntegerType = int64> 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<IntegerType> &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<IntegerType> bitset_;
std::vector<IntegerType> to_clear_;
DISALLOW_COPY_AND_ASSIGN(SparseBitset);
};
} // namespace operations_research
#endif // OR_TOOLS_UTIL_BITSET_H_