Dev: add GCC-10 and Clang-10 to local builds configs
[marnav.git] / include / marnav / utils / bitset.hpp
blob03f3691dbde6d4c215bdc820218188a92fd858fa
1 #ifndef MARNAV__UTILS__BITSET__HPP
2 #define MARNAV__UTILS__BITSET__HPP
4 /// Copyright (c) 2017 Mario Konrad <mario.konrad@gmx.net>
5 /// The code is licensed under the BSD License (see file LICENSE)
7 #include <limits>
8 #include <stdexcept>
9 #include <type_traits>
10 #include <vector>
11 #include <cassert>
13 namespace marnav
15 namespace utils
18 /// This is a dynamically growing bitset (theoretically of arbitrary size).
19 ///
20 /// Bits are stored in blocks, whose data type is configurable.
21 ///
22 /// This may not be the most performant implementation, but a very flexible one.
23 ///
24 /// This class suffers the same problems as std::vector<bool> does. Although is
25 /// provides an interator-like facility, it is not std algorithm compatible, nor
26 /// claims nor wants to be.
27 ///
28 /// @tparam Block The data type of the underlying block type.
29 ///
30 /// **Example:** appending individual bits
31 /// @code
32 /// bitset<uint32_t> bits;
33 /// bits.append(0xff, 8); // append 8 bits
34 /// bits.append(0xff, 6); // append 6 bits
35 /// bits.append(1, 1); // append 1 bit
36 /// bits.append(1, 1); // append 1 bit
37 /// auto result = bits.get<uint16_t>(0); // since all bits were 1, this will be 0xffff
38 /// @endcode
39 ///
40 /// **Example:** random access to bits
41 /// @code
42 /// bitset<uint32_t> bits;
43 /// bits.append(0);
44 /// auto bit = bits[7]; // indexed access (note: read only, unchecked)
45 /// bits.set(0xff, 3, 2); // set 2 bits (11) at offset 3
46 /// @endcode
47 ///
48 /// **Example:** bitset with initial number of bits
49 /// @code
50 /// bitset<uint8_t> bits(1024);
51 /// bits.set(1, 512, 1); // set one bit to 1 at offset 512
52 /// @endcode
53 ///
54 template <class Block,
55 class = typename std::enable_if<!std::numeric_limits<Block>::is_signed>::type>
56 class bitset
58 public:
59 using block_type = Block;
61 /// It is assumed, that a byte has 8 bits. This template will
62 /// not work on other architectures.
63 static constexpr auto bits_per_byte = 8u;
65 static constexpr auto bits_per_block = sizeof(block_type) * bits_per_byte;
67 public:
68 using container = std::vector<block_type>;
69 using size_type = typename container::size_type;
70 using data_const_iterator = typename container::const_iterator;
72 /// This is a non-std conform const iterator. The intention is
73 /// to provide a basic iterator functionality without claiming
74 /// to be std compliant.
75 class const_iterator
77 friend class bitset;
79 public:
80 using difference_type = long long;
81 using value_type = bool;
82 using iterator_category = std::random_access_iterator_tag;
84 private:
85 const bitset * bs; ///< Associated bitset.
86 size_type pos; ///< Position (bit) within the bitset.
88 private:
89 const_iterator(const bitset * const b, size_type p)
90 : bs(b)
91 , pos(p)
95 public:
96 const_iterator()
97 : bs(nullptr)
98 , pos(0)
102 const_iterator(const const_iterator &) = default;
103 const_iterator(const_iterator &&) noexcept = default;
105 size_type get_pos() const { return pos; }
107 const_iterator & operator=(const const_iterator &) = default;
108 const_iterator & operator=(const_iterator &&) noexcept = default;
110 bool operator==(const const_iterator & other) const noexcept
112 return bs == other.bs && pos == other.pos;
115 bool operator!=(const const_iterator & other) const noexcept
117 return bs != other.bs || pos != other.pos;
120 bool operator<(const const_iterator & other) const noexcept
122 return bs == other.bs && pos < other.pos;
125 bool operator>(const const_iterator & other) const noexcept
127 return bs == other.bs && pos > other.pos;
130 bool operator<=(const const_iterator & other) const noexcept
132 return bs == other.bs && pos <= other.pos;
135 bool operator>=(const const_iterator & other) const noexcept
137 return bs == other.bs && pos >= other.pos;
140 bool operator*() const
142 return bs != nullptr && pos < bs->size() && bs->get_bit(pos) == true;
145 const_iterator operator+(size_type n) const noexcept
147 return const_iterator{*this} += n;
150 const_iterator operator-(size_type n) const noexcept
152 return const_iterator{*this} -= n;
155 const_iterator & operator+=(size_type ofs)
157 if (bs != nullptr && pos < bs->size()) {
158 pos += ofs;
159 if (pos > bs->size())
160 pos = bs->size();
162 return *this;
165 const_iterator & operator-=(size_type ofs)
167 if (bs != nullptr) {
168 pos = (ofs > pos) ? 0 : pos - ofs;
170 return *this;
173 const_iterator & operator++() // ++const_iterator
175 if (bs != nullptr && pos < bs->size()) {
176 ++pos;
178 return *this;
181 const_iterator & operator--() // --const_iterator
183 if (bs != nullptr && pos > 0) {
184 --pos;
186 return *this;
189 const_iterator operator++(int) // const_iterator++
191 const_iterator res(*this);
192 if (bs != nullptr && pos < bs->size()) {
193 ++pos;
195 return res;
198 const_iterator operator--(int) // const_iterator--
200 const_iterator res(*this);
201 if (bs != nullptr && pos > 0) {
202 --pos;
204 return res;
207 /// Peeks from the bitset, but does not advance the iterator.
208 template <typename T> void peek(T & v, size_type bits = sizeof(T) * bits_per_byte) const
210 if (bs == nullptr)
211 return;
212 if (pos + bits > bs->size())
213 throw std::out_of_range{
214 "out of range, not possible to peek beyond available bits"};
215 bs->get(v, pos, bits);
218 /// Reads from the bitset and advances the iterator the number of read bits.
219 template <typename T> void read(T & v, size_type bits = sizeof(T) * bits_per_byte)
221 peek(v, bits);
222 *this += bits;
226 private:
227 size_type pos; // number of bits contained within the set
228 container data;
230 private:
231 /// Extends the container by the specified number of bits.
232 /// Extension is always one block.
233 void extend(size_type bits)
235 assert(bits > 0);
236 size_type n_blocks = (pos + bits + bits_per_block - 1) / bits_per_block;
237 if (n_blocks > data.size()) {
238 data.reserve(n_blocks);
239 while (bits > capacity() - pos) {
240 data.push_back(block_type{});
245 /// Appends the specified block to the data. The bitset is automatically extended
246 /// to hold all the data.
248 /// @param[in] v The data to append to the bitset.
249 /// @param[in] bits The number of bits to append. This value must be
250 /// between 0 and bits_per_block. If not all bits are being
251 /// appended, only the least significant bits are being taken.
252 void append_block(block_type v, size_type bits = bits_per_block)
254 assert(bits > 0);
255 extend(bits);
256 size_type i = pos / bits_per_block; // index of current block
258 // number of bits unused within the current block
259 size_type u_bits = bits_per_block - (pos % bits_per_block);
261 if (u_bits >= bits) {
262 // enough room within current block
263 data[i] |= v << (u_bits - bits);
264 } else {
265 // not enough room, split value to current and next block
266 data[i + 0] |= v >> (bits - u_bits);
267 data[i + 1] |= v << (bits_per_block - (bits - u_bits));
269 pos += bits;
272 /// Sets the specified block within the bit set. The bitset is automatically
273 /// extended to hold the data.
275 /// @param[in] v The data to be set.
276 /// @param[in] ofs The offset in bits at which the data has to be written.
277 /// @param[in] bits The number of bits of the data to be written
278 /// If not all bits are being written, only the least significant bits are being
279 /// taken.
280 /// @note This function does not extend the data container. It is assumed, that
281 /// this function runs in the context of another, which takes care of the
282 /// container (e.g. set_impl). This behaviour is intentional, because
283 /// this function runs potentially often, and therefore it is desired to
284 /// avoid additional runtime checks which can easily be done in the calling
285 /// environment.
286 void set_block(block_type v, size_type ofs, size_type bits = bits_per_block)
288 size_type i = ofs / bits_per_block; // index of current block
290 // number of bits unused within the current block
291 size_type u_bits = bits_per_block - (ofs % bits_per_block);
293 if (u_bits >= bits) {
294 // enough room within current block
295 block_type mask0 = -1;
296 mask0 <<= u_bits;
297 block_type mask1 = -1;
298 mask1 <<= u_bits - bits;
299 mask0 |= ~mask1;
300 v <<= u_bits - bits;
301 v &= ~mask0;
302 data[i] = (data[i] & mask0) | v;
303 } else {
304 // not enough room, split value to current and next block
305 block_type mask0 = ~((1 << (bits - u_bits)) - 1);
306 block_type mask1 = (1 << (bits_per_block - (bits - u_bits))) - 1;
308 data[i + 0] = (data[i + 0] & mask0) | v >> (bits - u_bits);
309 data[i + 1] = (data[i + 1] & mask1) | v << (bits_per_block - (bits - u_bits));
311 if (ofs + bits > pos)
312 pos = ofs + bits;
315 template <typename T>
316 void set_impl(T v, size_type ofs, size_type bits = sizeof(T) * bits_per_byte)
318 assert(bits > 0);
319 if (bits > sizeof(v) * bits_per_byte)
320 throw std::invalid_argument{"number of bit exceed number of available bits"};
321 if (ofs + bits > capacity())
322 extend(ofs + bits - capacity());
324 // fraction of the first block
325 size_type u_bits = bits_per_block - (ofs % bits_per_block); // part of the first block
326 if (u_bits > 0) {
327 if (bits <= u_bits) {
328 set_block(v, ofs, bits);
329 ofs += bits;
330 bits = 0;
331 } else {
332 set_block(v >> (bits - u_bits), ofs, u_bits);
333 ofs += u_bits;
334 bits -= u_bits;
338 // all complete blocks
339 for (; bits > bits_per_block; bits -= bits_per_block, ofs += bits_per_block) {
340 set_block(v >> (bits - bits_per_block), ofs);
343 // fraction of the last block
344 if (bits > 0) {
345 set_block(v << (bits_per_block - bits), ofs);
349 template <typename T> void set_dispatch(T v, size_type ofs, size_type bits, std::true_type)
351 set_impl(v, ofs, bits);
354 template <typename T> void set_dispatch(T v, size_type ofs, size_type bits, std::false_type)
356 set_impl(static_cast<typename std::underlying_type<T>::type>(v), ofs, bits);
359 /// Reads a block from the bit set.
361 /// @return The container to hold the data.
362 /// @param[in] ofs The offset in bits at which the data has to be read.
363 /// @param[in] bits Number of bits to be read.
364 /// If the number of bits is smaller than what the specified data can
365 /// hold, only the least significant bits are being set.
366 /// @exception std::out_of_range There are not enough bits to read, offset and number
367 /// of bits exceed the total number of available bits. It is not possible
368 /// to read past the end.
369 block_type get_block(size_type ofs, size_type bits = bits_per_block) const noexcept
371 assert(bits > 0);
372 const size_type i = ofs / bits_per_block; // index of current block
374 // number of bits unused within the current block
375 const size_type u_bits = bits_per_block - (ofs % bits_per_block);
377 if (u_bits >= bits) {
378 // desired data fully within the current block
379 block_type mask = (1 << u_bits) - 1;
380 return (data[i] & mask) >> (u_bits - bits);
381 } else {
382 // desired value is part from current block and part from next
383 block_type mask0 = (1 << u_bits) - 1;
384 block_type mask1 = ((1 << (bits_per_block - (bits - u_bits))) - 1)
385 << (bits - u_bits);
386 return (data[i + 0] & mask0) << (bits - u_bits)
387 | (data[i + 1] & mask1) >> (bits_per_block - (bits - u_bits));
391 /// Copies a block from source to destination offsets.
393 /// This is the equivalent of `set_block(get_block(...), ...)`
394 void copy_block(size_type r_ofs, size_type w_ofs, size_type bits = bits_per_block)
396 set_block(get_block(r_ofs, bits), w_ofs, bits);
399 public: // constructors
400 /// Copy constructor
401 bitset(const bitset &) = default;
403 /// Move constructor.
404 bitset(bitset &&) = default;
406 /// Copy assignment
407 bitset & operator=(const bitset &) = default;
409 /// Move assignment
410 bitset & operator=(bitset &&) = default;
412 /// Default construction
413 bitset()
414 : pos(0)
418 /// Construction with number of bits present (and usable) in the bitset.
419 /// All bits are initialized to the default constructed Block type..
421 /// @param[in] bits Number of bits (default initialized to 0) in the bitset.
422 explicit bitset(size_type bits)
423 : pos(0)
425 extend(bits);
426 pos = bits;
429 /// Construction with content from a specified container.
431 /// @param[in] begin Start position of the data (inclusive)
432 /// @param[in] end End position of the data (exclusive)
433 bitset(typename container::const_iterator begin, typename container::const_iterator end)
434 : pos((end - begin) * bits_per_block)
435 , data(begin, end)
439 /// Construction with move of the container, this does not copy any data.
440 explicit bitset(container && container)
441 : pos(container.size() * bits_per_block)
442 , data(std::move(container))
446 /// Constructs a bitset from the specified range.
448 /// It tries to copy blockwise.
449 bitset(const_iterator first, const_iterator last)
450 : pos(0)
452 if (last <= first)
453 return;
454 if (!first.bs)
455 return;
457 const size_type distance = last.pos - first.pos;
458 const size_type n_blocks = distance / bits_per_block;
459 const size_type u_bits = distance % bits_per_block;
461 if (u_bits > 0) {
462 data.reserve(n_blocks + 1);
463 } else {
464 data.reserve(n_blocks);
467 size_type r_ofs = first.pos;
468 for (size_type i = 0; i < n_blocks; ++i, r_ofs += bits_per_block)
469 data.push_back(first.bs->get_block(r_ofs, bits_per_block));
470 pos = n_blocks * bits_per_block;
472 if (u_bits > 0)
473 append(first.bs->get_block(r_ofs, u_bits), u_bits);
476 /// Constructs a bitset using the provided blocks.
477 bitset(std::initializer_list<Block> init_list)
478 : pos(0)
480 data.assign(init_list);
481 pos = data.size() * bits_per_block;
484 public: // container operations
485 /// Returns the capacity of this bit set. Note: not all bits must have
486 /// been occupied.
487 size_type capacity() const noexcept { return data.size() * bits_per_block; }
489 /// Returns the number of used bits.
490 size_type size() const noexcept { return pos; }
492 /// Reserves the number of blocks within this set.
494 /// @note This method reserves blocks, not bits.
495 void reserve(size_type blocks) { extend(blocks * bits_per_block); }
497 /// Clears the bit set.
498 void clear()
500 data.clear();
501 pos = 0;
504 public: // iterators
505 /// Returns a const iterator to the beginning of the data itself.
506 /// Note: this iterator accesses the data up to capacity(), some bits
507 /// may be unused at the end of the set.
508 data_const_iterator data_begin() const { return data.begin(); }
510 /// Returns a const iterator to the end of the data itself.
511 data_const_iterator data_end() const { return data.end(); }
513 const_iterator begin() const { return const_iterator(this, 0); }
515 const_iterator end() const { return const_iterator(this, size()); }
517 const_iterator cbegin() const { return const_iterator(this, 0); }
519 const_iterator cend() const { return const_iterator(this, size()); }
521 public: // append
522 /// Appends another bitset to this one.
524 /// @param[in] bs The bitset to be appended to this one.
526 /// @note It is not allowed to append a bitself to itself.
527 /// @note This algorithm is not efficient.
528 template <class U> void append(const bitset<U> & bs)
530 if (reinterpret_cast<const void *>(this) == reinterpret_cast<const void *>(&bs))
531 return;
532 for (const auto && bit : bs)
533 append(bit, 1);
536 /// Appends the lowest significant bits of the specified data to the
537 /// bit set. The set will be extended if necessary.
538 /// The second parameter specifies the number of bits to be used from
539 /// the given data, beginning at the lowest signnificant bit.
540 /// A size of 0 bits will have no effect.
542 /// @param[in] v The value to append to the bitset.
543 /// @param[in] bits Number of bits from the specified value. This must not
544 /// exceed the number of bits provided by the specified data,
545 /// padding is not supported.
546 /// @exception std::invalid_argument Number of bites exceed the number of
547 /// bit provided by the parameter v. padding is not implemented.
548 /// Example: type of v is uint32_t, bits is 40.
549 template <typename T> void append(T v, size_type bits = sizeof(T) * bits_per_byte)
551 if (bits <= 0)
552 return;
553 if (bits > sizeof(v) * bits_per_byte)
554 throw std::invalid_argument{"number of bits exceed number of available bits"};
555 size_type n_bits = bits % bits_per_block; // incomplete blocks
556 if (n_bits != 0) {
557 append_block(v >> (bits - n_bits), n_bits);
558 bits -= n_bits;
560 for (; bits > 0; bits -= bits_per_block) {
561 append_block(v >> (bits - bits_per_block));
565 public: // set
566 /// Sets the specified bitset at the offset within this bitset.
568 /// @param[in] bs The bitset to copy.
569 /// @param[in] ofs The offset within the bitset to copy the bitset
570 /// to. The entire specified bitset will be set.
572 /// @note It is not allowed to set a bitself to itself.
573 /// @note This algorithm is not efficient.
574 template <class U> void set(const bitset<U> & bs, size_type ofs)
576 if (reinterpret_cast<const void *>(this) == reinterpret_cast<const void *>(&bs))
577 return;
578 for (const auto & bit : bs) {
579 set(bit, ofs, 1);
580 ++ofs;
584 /// Sets bits within the set. The bitset is automatically exteneded to hold the data.
586 /// @param[in] v The value to set.
587 /// @param[in] ofs The offset (in bits) at which position the value has to be written.
588 /// @param[in] bits The number of bits to write. This must not exceed the number of bits
589 /// provided by the specified data, padding is not supported.
590 /// @exception std::invalid_argument Number of bites exceed the number of
591 /// bit provided by the parameter v. padding is not implemented.
592 /// Example: type of v is uint32_t, bits is 40.
593 template <typename T>
594 void set(T v, size_type ofs, size_type bits = sizeof(T) * bits_per_byte)
596 set_dispatch(v, ofs, bits, std::is_integral<T>{});
599 /// Resets the bit at the speficied index.
600 void reset(size_type index)
602 if (index >= size())
603 throw std::out_of_range{"index out of range in reset(index)"};
604 set(0, index, 1);
607 /// Sets all bits within the bitset to 0.
608 void reset() noexcept
610 for (auto & block : data)
611 block = 0;
614 /// Sets a single bit at a specified position to the specified value.
615 /// This method accesses the bitset as direct as possible and therefore does not
616 /// extend the set if the index is out of range.
618 /// @exception std::out_of_range Specified index is out of range.
619 void set_bit(size_type i, bool value)
621 if (i >= size())
622 throw std::out_of_range{"index out of range"};
624 // bit within the block to be read
625 size_type n_bit = bits_per_block - (i % bits_per_block) - 1;
626 if (value) {
627 data[i / bits_per_block] |= (1u << n_bit);
628 } else {
629 data[i / bits_per_block] &= ~(1u << n_bit);
633 public: // get
634 /// Returns the bit at the specified position. If the index is larger
635 /// than the actual number of bits, 'false' will rturn.
637 /// @exception std::out_of_range Specified index is out of range.
638 bool get_bit(size_type i) const
640 if (i >= size())
641 throw std::out_of_range{"index out of range"};
643 // bit within the block to be read
644 size_type n_bit = bits_per_block - (i % bits_per_block) - 1;
645 return ((data[i / bits_per_block] >> n_bit) & 1) ? true : false;
648 /// Simply an other name for get_bit.
649 bool test(size_type i) const { return get_bit(i); }
651 /// Reads data from the bit set. There must be enough capacity in either the
652 /// bitset to be read as well as the provided data type to contain the desired
653 /// number of bits.
655 /// @param[in] ofs The offset in bits at which position the data is to be read.
656 /// @param[in] bits Number of bits to be read. This must not exceeed the number of
657 /// bits the specified data type can hold.
658 /// If the number of bits is smaller than what the specified data can
659 /// hold, only the least significant bits are being set.
660 /// @return The data read from the bitset.
661 /// @exception std::invalid_argument Number of bites exceed the number of
662 /// bit provided by the return value. padding is not implemented.
663 /// Example: return value is of type uint32_t, bits is 40.
664 /// @exception std::out_of_range Offset and bits exceed the number of available
665 /// bits. It is not possible to read beyond the end.
666 template <class T>
667 typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value,
668 T>::type
669 get(size_type ofs, size_type bits = sizeof(T) * bits_per_byte) const
671 if (bits <= 0)
672 return T{};
673 if (bits > sizeof(T) * bits_per_byte)
674 throw std::invalid_argument{"number of bits (" + std::to_string(bits)
675 + ") exceed number of available bits ("
676 + std::to_string(sizeof(T) * bits_per_byte) + ")"};
677 if (ofs + bits > pos)
678 throw std::out_of_range{"offset (" + std::to_string(ofs) + ") and bits ("
679 + std::to_string(bits) + ") exceed available number of bits ("
680 + std::to_string(pos) + ")"};
682 T value = 0;
684 // number of bits unused within the current block
685 size_type u_bits = bits_per_block - (ofs % bits_per_block);
687 // fraction of the first block
688 if (u_bits > 0) {
689 auto block = get_block(ofs, u_bits);
690 if (bits < u_bits) {
691 block >>= (u_bits - bits);
692 bits = 0;
693 } else {
694 bits -= u_bits;
696 value += block;
697 ofs += u_bits;
700 // all complete blocks inbetween, only possible if sizeof(T) exceeds
701 // the block size (mupltiple blocks in one T).
702 // since this check is possible at compile time, modern compilers
703 // probably will eliminated it completely.
704 if (sizeof(T) * bits_per_byte > bits_per_block) {
705 for (; bits >= bits_per_block; bits -= bits_per_block) {
706 value <<= bits_per_block;
707 value += get_block(ofs);
708 ofs += bits_per_block;
712 // fraction of the last block
713 if (bits > 0) {
714 value <<= bits;
715 value += get_block(ofs, bits);
718 return value;
721 /// Specialization of `get` for enumerations.
722 template <class T>
723 typename std::enable_if<std::is_enum<T>::value, T>::type get(
724 size_type ofs, size_type bits = sizeof(T) * bits_per_byte) const
726 return static_cast<T>(get<typename std::underlying_type<T>::type>(ofs, bits));
729 /// Specialization of `get` for bool.
730 template <class T, typename std::enable_if<std::is_same<T, bool>::value, int>::type = 0>
731 void get(T & value, size_type ofs, size_type = 1) const
733 value = get_bit(ofs);
736 template <class T, typename std::enable_if<!std::is_same<T, bool>::value, int>::type = 0>
737 void get(T & value, size_type ofs, size_type bits = sizeof(T) * bits_per_byte) const
739 value = get<T>(ofs, bits);
742 bool get(size_type ofs) const { return get_bit(ofs); }
744 public: // access operators
745 /// Returns the bit at the specified position.
746 bool operator[](size_type i) const { return get_bit(i); }
748 public: // comparison operators
749 /// Equality comparison operator for the same bitset type.
750 bool operator==(const bitset & other) const { return this == &other || data == other.data; }
752 /// Inequality comparison operator for the same bitset type.
753 bool operator!=(const bitset & other) const { return !(*this == other); }
755 /// Equalily comparison operator for different bitset types.
757 /// Since the blocks differ, a comparison bit by bit is done.
759 /// @note This is implemented for readablility, not max performance.
760 template <class XBlock,
761 class = typename std::enable_if<!std::numeric_limits<XBlock>::is_signed>::type>
762 bool operator==(const bitset<XBlock> & other) const
764 if (size() != other.size())
765 return false;
767 auto i = begin();
768 auto j = other.begin();
769 while (i != end()) {
770 if (*i != *j)
771 return false;
772 ++i;
773 ++j;
775 return true;
778 /// Inequality comparison operator for different bitset types.
780 /// Since the blocks differ, a comparison bit by bit is done.
782 /// @note This is implemented for readablility, not max performance.
783 template <class XBlock,
784 class = typename std::enable_if<!std::numeric_limits<XBlock>::is_signed>::type>
785 bool operator!=(const bitset<XBlock> & other) const
787 return !(*this == other);
790 /// Comparinson for 'less'.
791 bool operator<(const bitset & other) const
793 // are there any bits in the range of one bitset that exceeds the other
794 if (size() > other.size()) {
795 if (any(begin() + (size() - other.size()), end()))
796 return false;
797 } else if (other.size() > size()) {
798 if (other.any(other.begin() + (other.size() - size()), other.end()))
799 return true;
802 // check all blocks from the most to the least significant.
803 for (auto i = size() / bits_per_block; i > 0; --i) {
804 if (data[i - 1] < other.data[i - 1])
805 return true;
808 // no difference found, they are the same
809 return false;
812 /// Comparinson for 'less or equal'.
813 bool operator<=(const bitset & other) const
815 // are there any bits in the range of one bitset that exceeds the other
816 if (size() > other.size()) {
817 if (any(begin() + (size() - other.size()), end()))
818 return false;
819 } else if (other.size() > size()) {
820 if (other.any(other.begin() + (other.size() - size()), other.end()))
821 return true;
824 // check all blocks from the most to the least significant.
825 for (auto i = size() / bits_per_block; i > 0; --i) {
826 if (data[i - 1] > other.data[i - 1])
827 return false;
830 // no difference found, they are the same
831 return true;
834 /// Comparinson for 'greater'.
835 bool operator>(const bitset & other) const { return !(*this <= other); }
837 /// Comparinson for 'greater or equal'.
838 bool operator>=(const bitset & other) const { return !(*this < other); }
840 public: // arithmetic operators
841 /// Increments the bitset by one. If an overflow is to occurr, the bitset
842 /// resets to 0 and continues counting.
843 bitset & operator++() // ++bitset
845 if (size() <= 0)
846 return *this;
848 const size_type u_bits = size() % bits_per_block;
850 // parts of a block
851 if (u_bits > 0) {
852 size_type ofs = bits_per_block * (size() / bits_per_block);
854 block_type block = get_block(ofs, u_bits);
856 if (block < ((block_type{1} << u_bits) - 1)) {
857 ++block;
858 set_block(block, ofs, u_bits);
859 return *this;
861 block = block_type{}; // maximum reached, overflow to the next block
862 set_block(block, ofs, u_bits);
865 // full blocks
866 for (size_type i = size() / bits_per_block; i > 0; --i) {
867 if (data[i - 1] < std::numeric_limits<block_type>::max()) {
868 ++data[i - 1];
869 return *this;
871 data[i - 1] = block_type{};
874 return *this;
877 bitset operator++(int) // bitset++
879 bitset result{*this};
880 ++(*this);
881 return result;
884 /// Decrements the bitset by one. If an underflow is to occurr, the bitset
885 /// resets to all 1es and continues counting.
886 bitset & operator--() // --bitset
888 if (size() <= 0)
889 return *this;
891 const size_type u_bits = size() % bits_per_block;
893 // parts of a block
894 if (u_bits > 0) {
895 size_type ofs = bits_per_block * (size() / bits_per_block);
897 block_type block = get_block(ofs, u_bits);
899 if (block > 0) {
900 --block;
901 set_block(block, ofs, u_bits);
902 return *this;
904 block = (block_type{1} << u_bits) - 1; // max reached, overflow to the next block
905 set_block(block, ofs, u_bits);
908 // full blocks
909 for (size_type i = size() / bits_per_block; i > 0; --i) {
910 if (data[i - 1] > 0) {
911 --data[i - 1];
912 return *this;
914 data[i - 1] = std::numeric_limits<block_type>::max();
917 return *this;
920 bitset operator--(int) // bitset--
922 bitset result{*this};
923 --(*this);
924 return result;
927 public: // shift operator
928 /// Bit shift left. This function tries to shift entire blocks at once.
929 bitset & shl(size_type bits)
931 if (bits >= size()) {
932 reset();
933 return *this;
936 // copy all bits necessary, block wise.
937 size_type r_ofs = bits;
938 size_type w_ofs = 0;
939 while (r_ofs < size()) {
941 // this resembles std::min, but std::min complains about
942 // bits_per_block being an undefined reference.
943 const size_type d = size() - r_ofs;
944 size_type u_bits = d < bits_per_block ? d : bits_per_block;
946 copy_block(r_ofs, w_ofs, u_bits);
947 r_ofs += u_bits;
948 w_ofs += u_bits;
951 // overwrite all remaining bits with zeroes.
952 while (w_ofs < size()) {
954 // same issue with std::min here.
955 const size_type d = size() - w_ofs;
956 size_type u_bits = d < bits_per_block ? d : bits_per_block;
958 set_block(0, w_ofs, u_bits);
959 w_ofs += bits_per_block;
962 return *this;
965 bitset & operator<<=(size_type bits) { return shl(bits); }
967 bitset operator<<(size_type bits) const { return bitset{*this} <<= bits; }
969 /// Bit shift right. This function tries to shift entire blocks
970 /// at once.
971 bitset & shr(size_type bits)
973 if (bits >= size()) {
974 reset();
975 return *this;
978 size_type r_ofs = 0;
979 size_type w_ofs = 0;
981 // first: handle the last bits (probably partially a block, or a single block)
982 size_type u_bits = size() % bits_per_block;
983 if (u_bits > 0) {
984 if (u_bits == size()) {
985 u_bits = size() - bits;
986 r_ofs = 0;
987 w_ofs = bits;
988 } else {
989 r_ofs = size() - u_bits - bits;
990 w_ofs = size() - u_bits;
992 } else {
993 if (size() == bits_per_block) {
994 u_bits = bits_per_block - bits;
995 r_ofs = 0;
996 w_ofs = bits;
997 } else {
998 u_bits = bits_per_block;
999 r_ofs = size() - bits_per_block - bits;
1000 w_ofs = size() - bits_per_block;
1003 copy_block(r_ofs, w_ofs, u_bits);
1005 // second: handle whole blocks
1006 while (r_ofs > 0) {
1007 if (r_ofs > bits_per_block) {
1008 u_bits = bits_per_block;
1009 r_ofs -= bits_per_block;
1010 w_ofs -= bits_per_block;
1011 } else {
1012 u_bits = r_ofs;
1013 r_ofs = 0;
1014 w_ofs -= u_bits;
1016 copy_block(r_ofs, w_ofs, u_bits);
1019 // third: reset all bits remaining in front
1020 while (w_ofs > 0) {
1021 u_bits = (w_ofs < bits_per_block) ? w_ofs : bits_per_block;
1022 w_ofs -= u_bits;
1023 set_block(0, w_ofs, u_bits);
1026 return *this;
1029 bitset & operator>>=(size_type bits) { return shr(bits); }
1031 bitset operator>>(size_type bits) const { return bitset{*this} >>= bits; }
1033 public: // logic operator
1034 /// Logical or operation. The resulting bitset is of the size of the larger one.
1036 /// Example: '0011' | '11000' = '11110'
1037 bitset & operator|=(const bitset & other)
1039 if (size() < other.size()) {
1040 extend(other.size() - size());
1041 pos = other.size();
1044 for (auto i = 0u; i < data.size(); ++i)
1045 data[i] |= other.data[i];
1047 return *this;
1050 bitset operator|(const bitset & other) const { return bitset{*this} |= other; }
1052 /// Logical or operation. The resulting bitset is of the size of the smaller one.
1054 /// Example: '0111' & '11001' = '0100'
1055 bitset & operator&=(const bitset & other)
1057 if (size() > other.size()) {
1058 for (auto i = other.size(); i < size(); ++i)
1059 reset(i);
1060 pos = other.size();
1063 for (auto i = 0u; i < data.size(); ++i)
1064 data[i] &= other.data[i];
1066 return *this;
1069 bitset operator&(const bitset & other) const { return bitset{*this} &= other; }
1071 /// Logical xor operation. The resulting bitset is of the size of the larger one.
1073 /// Example: '0011' ^ '11100' = '11011'
1074 bitset & operator^=(const bitset & other)
1076 if (size() < other.size()) {
1077 extend(other.size() - size());
1078 pos = other.size();
1081 for (auto i = 0u; i < data.size(); ++i)
1082 data[i] ^= other.data[i];
1084 return *this;
1087 bitset operator^(const bitset & other) const { return bitset{*this} ^= other; }
1089 public: // other
1090 /// Flips the bit at the specified index.
1092 /// @exception std::out_of_range Specified index is out of range.
1093 void flip(size_type i) { set(!get_bit(i), i, 1); }
1095 /// Returns true if all bits are true.
1097 /// @note This is implemented for readablility, not max performance.
1098 bool all() const noexcept
1100 for (auto i = begin(); i != end(); ++i)
1101 if (*i == false)
1102 return false;
1103 return true;
1106 /// Returns true if any of the bits are true.
1108 /// @note This is implemented for readablility, not max performance.
1109 bool any() const noexcept { return any(begin(), end()); }
1111 /// Returns true if any of the bits in the specified range are true.
1113 /// @note This is implemented for readablility, not max performance.
1114 bool any(const_iterator first, const_iterator last) const noexcept
1116 for (auto i = first; i != last; ++i)
1117 if (*i == true)
1118 return true;
1119 return false;
1122 /// Returns true if none of the bits are true.
1124 /// @note This is implemented for readablility, not max performance.
1125 bool none() const noexcept { return none(begin(), end()); }
1127 /// Returns true if none of the bits within the specified range are true.
1129 /// @note This is implemented for readablility, not max performance.
1130 bool none(const_iterator first, const_iterator last) const noexcept
1132 for (auto i = first; i != last; ++i)
1133 if (*i == true)
1134 return false;
1135 return true;
1138 /// Returns the number of bits set to true.
1140 /// @note This is implemented for readablility, not max performance.
1141 size_type count() const noexcept { return count(begin(), end()); }
1143 /// Returns the number of bits between the specified iterators.
1145 /// @param[in] first Ponints to the first bit of the range to test.
1146 /// @param[in] last Ponits to the bit after the range.
1148 /// @note This is implemented for readablility, not max performance.
1149 size_type count(const_iterator first, const_iterator last) const noexcept
1151 size_type n = 0;
1152 for (auto i = first; i != last; ++i)
1153 if (*i == true)
1154 ++n;
1155 return n;
1161 #endif