//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2020 James D. Mitchell
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/ >.
//
// TODO(later)
// 1) doc
#ifndef LIBSEMIGROUPS_BITSET_HPP_
#define LIBSEMIGROUPS_BITSET_HPP_
#include <array> // for array
#include <bitset> // for bitset
#include <climits> // for CHAR_BIT
#include <cstddef> // for size_t
#include <iosfwd> // for operator<<, ostringstream
#include <type_traits> // for false_type
#include <utility> // for hash
#include "config.hpp" // for LIBSEMIGROUPS_SIZEOF_VOID_P
#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
#include "string.hpp" // for detail::to_string
namespace libsemigroups {
// The code below for popcnt is borrowed/adapted from GAP.
#if LIBSEMIGROUPS_USE_POPCNT && defined (LIBSEMIGROUPS_HAVE___BUILTIN_POPCOUNTL)
template <typename T>
static inline size_t COUNT_TRUES_BLOCK(T block) {
return __builtin_popcountl(block);
}
#else
#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
template <typename T>
static inline auto COUNT_TRUES_BLOCK(T block)
-> std::enable_if_t<sizeof (T) == 8, size_t> {
block
= (block & 0x5555555555555555L) + ((block >> 1) & 0x5555555555555555L);
block
= (block & 0x3333333333333333L) + ((block >> 2) & 0x3333333333333333L);
block = (block + (block >> 4)) & 0x0f0f0f0f0f0f0f0fL;
block = (block + (block >> 8));
block = (block + (block >> 16));
block = (block + (block >> 32)) & 0x00000000000000ffL;
return block;
}
#endif
template <typename T>
static inline auto COUNT_TRUES_BLOCK(T block)
-> std::enable_if_t<sizeof (T) == 4, size_t> {
block = (block & 0x55555555) + ((block >> 1) & 0x55555555);
block = (block & 0x33333333) + ((block >> 2) & 0x33333333);
block = (block + (block >> 4)) & 0x0f0f0f0f;
block = (block + (block >> 8));
block = (block + (block >> 16)) & 0x000000ff;
return block;
}
template <typename T>
static inline auto COUNT_TRUES_BLOCK(T block)
-> std::enable_if_t<sizeof (T) == 2, size_t> {
block = (block & 0x5555) + ((block >> 1) & 0x5555);
block = (block & 0x3333) + ((block >> 2) & 0x3333);
block = (block + (block >> 4)) & 0x0f0f;
block = (block + (block >> 8)) & 0x00ff;
return block;
}
template <typename T>
static inline auto COUNT_TRUES_BLOCK(T block)
-> std::enable_if_t<sizeof (T) == 1, size_t> {
static constexpr std::array<size_t, 256> const lookup = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4,
2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4,
2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5,
3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
return lookup[block];
}
#endif
template <size_t N>
class BitSet {
static_assert(N > 0, "BitSet does not support 0 entries" );
#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
static_assert(N <= 64, "BitSet does not support more than 64 entries" );
#else
static_assert(N <= 32, "BitSet does not support more than 32 entries" );
#endif
public :
using block_type = std::conditional_t<
N <= 8,
uint_fast8_t,
std::conditional_t<
N <= 16,
uint_fast16_t,
std::conditional_t<N <= 32, uint_fast32_t, uint64_t>>>;
explicit constexpr BitSet(block_type block) noexcept : _block(block) {}
constexpr BitSet() noexcept : BitSet(0) {}
constexpr BitSet(BitSet const &) noexcept = default ;
constexpr BitSet(BitSet&&) noexcept = default ;
BitSet& operator =(BitSet const &) noexcept = default ;
BitSet& operator =(BitSet&&) noexcept = default ;
template <typename T>
BitSet(T first, T last) : BitSet() {
LIBSEMIGROUPS_ASSERT(first <= last);
size_t const K = std::distance(first, last);
if (K > size()) {
LIBSEMIGROUPS_EXCEPTION(
"the size of the container is %llu, trying to initialize with %llu "
"items" ,
static_cast <uint64_t>(size()),
static_cast <uint64_t>(K))
}
auto it = first;
for (size_t i = 0; i < K; ++i, ++it) {
set(i, *it);
}
}
~BitSet() = default ;
// Could be static
constexpr size_t size() const noexcept {
return N;
}
static constexpr size_t max_size() noexcept {
#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
return 64;
#else
return 32;
#endif
}
bool operator <(BitSet const & that) const noexcept {
clear_hi_bits();
that.clear_hi_bits();
return _block < that._block;
}
bool operator ==(BitSet const & that) const noexcept {
clear_hi_bits();
that.clear_hi_bits();
return _block == that._block;
}
bool operator !=(BitSet const & that) const noexcept {
return !(*this == that);
}
void operator &=(BitSet const & that) const noexcept {
_block &= that._block;
}
BitSet<N> operator &(BitSet const & that) const noexcept {
return BitSet(_block & that._block);
}
void operator |=(BitSet const & that) const noexcept {
_block |= that._block;
}
bool test(size_t pos) const noexcept {
LIBSEMIGROUPS_ASSERT(pos < N);
return _block & mask(pos);
}
bool operator [](size_t pos) const noexcept {
return test(pos);
}
BitSet& set() noexcept {
_block = ~0;
return *this ;
}
BitSet& set(size_t pos, bool value = true ) noexcept {
LIBSEMIGROUPS_ASSERT(pos < N);
if (value) {
_block |= mask(pos);
} else {
_block &= ~mask(pos);
}
return *this ;
}
BitSet& set(size_t first, size_t last, bool value) noexcept {
LIBSEMIGROUPS_ASSERT(first < N);
LIBSEMIGROUPS_ASSERT(last <= N);
LIBSEMIGROUPS_ASSERT(first < last);
block_type m = ~0;
m = (m >> first);
m = (m << (first + (block_count() - last)));
m = (m >> (block_count() - last));
if (value) {
_block |= m;
} else {
_block &= ~m;
}
return *this ;
}
BitSet& reset() noexcept {
_block = 0;
return *this ;
}
BitSet& reset(size_t pos) noexcept {
LIBSEMIGROUPS_ASSERT(pos < N);
_block &= ~mask(pos);
return *this ;
}
BitSet& reset(size_t first, size_t last) {
LIBSEMIGROUPS_ASSERT(first < N);
LIBSEMIGROUPS_ASSERT(last <= N);
LIBSEMIGROUPS_ASSERT(first < last);
return set(first, last, false );
}
size_t count() const noexcept {
clear_hi_bits();
return COUNT_TRUES_BLOCK(_block);
}
template <typename S>
void apply(S&& func) const {
#if LIBSEMIGROUPS_USE_CLZLL && defined (LIBSEMIGROUPS_HAVE___BUILTIN_CLZLL)
block_type block = _block;
while (block != 0) {
block_type t = block & -block;
size_t i = static_cast <size_t>(__builtin_ctzll(block));
if (i >= size()) {
break ;
}
func(i);
block ^= t;
}
#else
for (size_t i = 0; i < size(); ++i) {
if (test(i)) {
func(i);
}
}
#endif
}
block_type to_int() const noexcept {
clear_hi_bits();
return _block;
}
friend std::ostringstream& operator <<(std::ostringstream& os,
BitSet<N> const & bs) {
for (size_t i = 0; i < N; ++i) {
os << bs.test(i);
}
return os;
}
friend std::ostream& operator <<(std::ostream& os, BitSet<N> const & bs) {
os << detail::to_string(bs);
return os;
}
private :
void clear_hi_bits() const noexcept {
size_t s = block_count() - N;
_block = _block << s;
_block = _block >> s;
}
constexpr size_t block_count() const noexcept {
return sizeof (block_type) * CHAR_BIT;
}
constexpr block_type mask(size_t i) const noexcept {
// LIBSEMIGROUPS_ASSERT(i < size());
return static_cast <block_type>(MASK[i]);
}
static constexpr uint64_t MASK[64] = {0x1,
0x2,
0x4,
0x8,
0x10,
0x20,
0x40,
0x80,
0x100,
0x200,
0x400,
0x800,
0x1000,
0x2000,
0x4000,
0x8000,
0x10000,
0x20000,
0x40000,
0x80000,
0x100000,
0x200000,
0x400000,
0x800000,
0x1000000,
0x2000000,
0x4000000,
0x8000000,
0x10000000,
0x20000000,
0x40000000,
0x80000000,
0x100000000,
0x200000000,
0x400000000,
0x800000000,
0x1000000000,
0x2000000000,
0x4000000000,
0x8000000000,
0x10000000000,
0x20000000000,
0x40000000000,
0x80000000000,
0x100000000000,
0x200000000000,
0x400000000000,
0x800000000000,
0x1000000000000,
0x2000000000000,
0x4000000000000,
0x8000000000000,
0x10000000000000,
0x20000000000000,
0x40000000000000,
0x80000000000000,
0x100000000000000,
0x200000000000000,
0x400000000000000,
0x800000000000000,
0x1000000000000000,
0x2000000000000000,
0x4000000000000000,
0x8000000000000000};
mutable block_type _block;
};
template <size_t N>
constexpr uint64_t BitSet<N>::MASK[64];
namespace detail {
template <typename T>
struct IsBitSetHelper : std::false_type {};
template <size_t N>
struct IsBitSetHelper<BitSet<N>> : std::true_type {};
} // namespace detail
template <typename T>
static constexpr bool IsBitSet = detail::IsBitSetHelper<T>::value;
namespace detail {
struct LessBitSet {
// not noexcept because std::bitset<N>::to_ullong throws
// std::overflow_error if N exceeds the capacity of a unsigned long
// long.
template <size_t N>
bool operator ()(std::bitset<N> const & x, std::bitset<N> const & y) const {
return x.to_ullong() < y.to_ullong();
}
template <size_t N>
bool operator ()(BitSet<N> const & x, BitSet<N> const & y) const noexcept {
return x < y;
}
};
} // namespace detail
} // namespace libsemigroups
namespace std {
template <size_t N>
struct hash<libsemigroups::BitSet<N>> {
using block_type = typename libsemigroups::BitSet<N>::block_type;
size_t operator ()(libsemigroups::BitSet<N> const & bs) const noexcept(
std::is_nothrow_default_constructible<hash<block_type>>::value) {
return hash<block_type>()(bs.to_int());
}
};
} // namespace std
#endif // LIBSEMIGROUPS_BITSET_HPP_
quality 98%
¤ Dauer der Verarbeitung: 0.15 Sekunden
¤
*© Formatika GbR, Deutschland