GF2++
Loading...
Searching...
No Matches
BitStore.h
Go to the documentation of this file.
1#pragma once
2// SPDX-FileCopyrightText: 2025 Nessan Fitzmaurice <nzznfitz+gh@icloud.com>
3// SPDX-License-Identifier: MIT
4
8
9#include <gf2/BitRef.h>
10#include <gf2/assert.h>
11#include <gf2/Iterators.h>
12#include <gf2/RNG.h>
13#include <gf2/unsigned_word.h>
14#include <gf2/store_word.h>
15
16#include <bitset>
17#include <concepts>
18#include <format>
19#include <iostream>
20#include <limits>
21#include <optional>
22#include <ranges>
23#include <string>
24namespace gf2 {
25
34template<typename Store>
35class BitStore {
36private:
37 // CRTP helper method that returns a reference to the derived type.
38 constexpr Store& store() { return static_cast<Store&>(*this); }
39
40 // CRTP helper method that returns a const reference to the derived type.
41 constexpr const Store& store() const { return static_cast<const Store&>(*this); }
42
43public:
45 using word_type = typename store_word<Store>::word_type;
46
48 static constexpr u8 bits_per_word = BITS<word_type>;
49
50 // A default constructor plus the rule of 5 to stop picky compilers from complaining ...
51 BitStore() = default;
52 virtual ~BitStore() = default;
53 BitStore(const BitStore&) = default;
54 BitStore& operator=(const BitStore&) = default;
55 BitStore(BitStore&&) = default;
56 BitStore& operator=(BitStore&&) = default;
57
61
63 constexpr usize size() const { return store().size(); }
64
69 constexpr usize words() const { return store().words(); }
70
81 constexpr word_type word(usize i) const { return store().word(i); }
82
93 constexpr void set_word(usize i, word_type value) { store().set_word(i, value); }
94
96 constexpr const word_type* data() const { return store().data(); }
97
101 constexpr word_type* data() { return store().data(); }
102
106 constexpr u8 offset() const { return store().offset(); }
107
111
123 constexpr bool get(usize i) const {
124 gf2_debug_assert(i < size(), "Index {} is out of bounds for a bit store of length {}", i, size());
125 auto [word_index, mask] = index_and_mask<word_type>(i);
126 return word(word_index) & mask;
127 }
128
141 constexpr bool operator[](usize index) const { return get(index); }
142
154 constexpr bool front() const {
155 gf2_debug_assert(!is_empty(), "The store is empty!");
156 return get(0);
157 }
158
170 constexpr bool back() const {
171 gf2_debug_assert(!is_empty(), "The store is empty!");
172 return get(size() - 1);
173 }
174
178
191 auto set(usize index, bool value = true) {
192 gf2_debug_assert(index < size(), "Index {} is out of bounds for a bit store of length {}", index, size());
193 auto [word_index, mask] = index_and_mask<word_type>(index);
194 auto word_value = word(word_index);
195 auto bit_value = (word_value & mask) != 0;
196 if (bit_value != value) set_word(word_index, word_value ^ mask);
197 return *this;
198 }
199
217 constexpr auto operator[](usize index) { return BitRef<Store>{this, index}; }
218
233 auto flip(usize index) {
234 gf2_debug_assert(index < size(), "Index {} is out of bounds for a bit store of length {}", index, size());
235 auto [word_index, mask] = index_and_mask<word_type>(index);
237 return *this;
238 }
239
258 constexpr auto swap(usize i0, usize i1) {
259 gf2_debug_assert(i0 < size(), "index {} is out of bounds for a store of length {}", i0, size());
260 gf2_debug_assert(i1 < size(), "index {} is out of bounds for a store of length {}", i1, size());
261 if (i0 != i1) {
262 auto [w0, mask0] = index_and_mask<word_type>(i0);
263 auto [w1, mask1] = index_and_mask<word_type>(i1);
264 auto word0 = word(w0);
265 auto word1 = word(w1);
266 auto val0 = (word0 & mask0) != 0;
267 auto val1 = (word1 & mask1) != 0;
268 if (val0 != val1) {
269 if (w0 == w1) {
270 // Both bits are in the same word
271 set_word(w0, word0 ^ mask0 ^ mask1);
272 } else {
273 // BitsIter are in different words
274 set_word(w0, word0 ^ mask0);
275 set_word(w1, word1 ^ mask1);
276 }
277 }
278 }
279 return *this;
280 }
281
285
295 constexpr bool is_empty() const { return size() == 0; }
296
308 constexpr bool any() const {
309 for (auto i = 0uz; i < words(); ++i)
310 if (word(i) != 0) return true;
311 return false;
312 }
313
327 constexpr bool all() const {
328 auto num_words = words();
329 if (num_words == 0) { return true; }
330
331 // Check the fully occupied words ...
332 for (auto i = 0uz; i < num_words - 1; ++i) {
333 if (word(i) != MAX<word_type>) return false;
334 }
335
336 // Last word might not be fully occupied ...
337 auto unused_bits = bits_per_word - (size() % bits_per_word);
338 auto last_word_max = MAX<word_type> >> unused_bits;
339 if (word(num_words - 1) != last_word_max) return false;
340
341 return true;
342 }
343
355 constexpr bool none() const { return !any(); }
356
360
371 auto set_all(bool value = true) {
372 auto word_value = value ? MAX<word_type> : word_type{0};
373 for (auto i = 0uz; i < words(); ++i) set_word(i, word_value);
374 return *this;
375 }
376
385 auto flip_all() {
386 for (auto i = 0uz; i < words(); ++i) set_word(i, static_cast<word_type>(~word(i)));
387 return *this;
388 }
389
393
411 template<unsigned_word Src>
412 auto copy(Src src) {
413 constexpr auto src_bits = BITS<Src>;
414 gf2_assert(size() == src_bits, "Lengths do not match: {} != {}.", size(), src_bits);
415
416 if constexpr (src_bits <= bits_per_word) {
417 // The source fits into a single one of our words
418 set_word(0, static_cast<word_type>(src));
419 } else {
420 // The source spans some number of our words (which we assume is an integer number).
421 constexpr auto num_words = src_bits / bits_per_word;
422 for (auto i = 0uz; i < num_words; ++i) {
423 auto shift = i * bits_per_word;
424 auto word_value = static_cast<word_type>((src >> shift) & MAX<word_type>);
425 set_word(i, word_value);
426 }
427 }
428 return *this;
429 }
430
445 template<typename SrcStore>
446 auto copy(const BitStore<SrcStore>& src) {
447 // The sizes must match ...
448 gf2_assert(size() == src.size(), "Lengths do not match: {} != {}.", size(), src.size());
449
450 // What is the source word type (without any const qualifier)?
451 using src_word_type = std::remove_const_t<typename store_word<SrcStore>::word_type>;
452
453 // Numbers of bits per respective words
454 constexpr auto word_bits = BITS<word_type>;
455 constexpr auto src_word_bits = BITS<src_word_type>;
456
457 // What we do next depends on the size of the respective words.
458 if constexpr (word_bits == src_word_bits) {
459 // Easiest case: Same sized words -- we can copy whole words directly ...
460 for (auto i = 0uz; i < words(); ++i) {
461 auto src_word = static_cast<word_type>(src.word(i));
462 set_word(i, src_word);
463 }
464 } else if constexpr (word_bits > src_word_bits) {
465 // Our word type is larger -- some number of source words fit into each one of ours.
466 // We assume that our word size is an integer multiple of the source word size.
467 constexpr auto ratio = word_bits / src_word_bits;
468
469 auto src_words = src.words();
470 for (auto i = 0uz; i < words(); ++i) {
471
472 // Combine `ratio` source words into a single destination word being careful not to run off the end ...
473 word_type dst_word = 0;
474 for (auto j = 0uz; j < ratio; ++j) {
475 auto src_index = i * ratio + j;
476 word_type src_word = 0;
477 if (src_index < src_words) src_word = static_cast<word_type>(src.word(src_index));
478 dst_word |= (src_word << (j * src_word_bits));
479 }
480 set_word(i, dst_word);
481 }
482 } else {
483 // Our word size is smaller -- each source word becomes multiple destination words.
484 // We assume that the source word size is an integer multiple of our word size.
485 constexpr auto ratio = src_word_bits / word_bits;
486
487 auto dst_words = words();
488 for (auto src_index = 0uz; src_index < src.words(); ++src_index) {
489 auto src_word = src.word(src_index);
490
491 // Split the source word into `ratio` destination words ...
492 for (auto j = 0uz; j < ratio; ++j) {
493 auto dst_index = src_index * ratio + j;
494 if (dst_index >= dst_words) break;
495 auto shift = j * word_bits;
496 auto dst_word = static_cast<word_type>((src_word >> shift) & MAX<word_type>);
497 set_word(dst_index, dst_word);
498 }
499 }
500 }
501 return *this;
502 }
503
515 template<usize N>
516 auto copy(const std::bitset<N>& src) {
517 // The sizes must match
518 gf2_assert(size() == N, "Lengths do not match: {} != {}.", size(), N);
519
520 // Doing this the dumb way as there is no standard access to the bitset's underlying store
521 set_all(false);
522 for (auto i = 0uz; i < N; ++i)
523 if (src[i]) set(i);
524 return *this;
525 }
526
536 auto fill(std::invocable<usize> auto f) {
537 set_all(false);
538 for (auto i = 0uz; i < size(); ++i)
539 if (f(i) == true) set(i);
540 return *this;
541 }
542
560 auto random_fill(double p = 0.5, u64 seed = 0) {
561 // Keep a single static RNG per thread for all calls to this method, seeded with entropy on the first call.
562 thread_local RNG rng;
563
564 // Edge case handling ...
565 if (p < 0) return set_all(false);
566
567 // Scale p by 2^64 to remove floating point arithmetic from the main loop below.
568 // If we determine p rounds to 1 then we can just set all elements to 1 and return early.
569 p = p * 0x1p64 + 0.5;
570 if (p >= 0x1p64) return set_all();
571
572 // p does not round to 1 so we use a 64-bit URNG and check each draw against the 64-bit scaled p.
573 auto scaled_p = static_cast<u64>(p);
574
575 // If a seed was provided, set the RNG's seed to it. Otherwise, we carry on from where we left off.
576 u64 old_seed = rng.seed();
577 if (seed != 0) rng.set_seed(seed);
578
579 set_all(false);
580 for (auto i = 0uz; i < size(); ++i)
581 if (rng.u64() < scaled_p) set(i);
582
583 // Restore the old seed if necessary.
584 if (seed != 0) rng.set_seed(old_seed);
585 return *this;
586 }
587
591
601 constexpr usize count_ones() const {
602 usize count = 0;
603 for (auto i = 0uz; i < words(); ++i) count += static_cast<usize>(gf2::count_ones(word(i)));
604 return count;
605 }
606
616 constexpr usize count_zeros() const { return size() - count_ones(); }
617
629 constexpr usize leading_zeros() const {
630 // Note: Even if the last word is not fully occupied, we know unused bits are set to 0.
631 for (auto i = 0uz; i < words(); ++i) {
632 auto w = word(i);
633 if (w != 0) return i * bits_per_word + static_cast<usize>(gf2::trailing_zeros(w));
634 }
635 return size();
636 }
637
647 constexpr usize trailing_zeros() const {
648 if (is_empty()) return 0;
649
650 // The last word may not be fully occupied so we need to subtract the number of unused bits.
651 auto unused_bits = bits_per_word - (size() % bits_per_word);
652 auto num_words = words();
653 auto i = num_words;
654 while (i--) {
655 auto w = word(i);
656 if (w != 0) {
657 auto whole_count = (num_words - i - 1) * bits_per_word;
658 auto partial_count = static_cast<usize>(gf2::leading_zeros(w)) - unused_bits;
659 return whole_count + partial_count;
660 }
661 }
662 return size();
663 }
664
668
684 std::optional<usize> first_set() const {
685 // Iterate forward looking for a word with a set bit and use the lowest of those ...
686 // Remember that any unused bits in the final word are guaranteed to be unset.
687 for (auto i = 0uz; i < words(); ++i) {
688 if (auto loc = lowest_set_bit(word(i))) return i * bits_per_word + loc.value();
689 }
690 return {};
691 }
692
706 std::optional<usize> last_set() const {
707 // Iterate backward looking for a word with a set bit and use the highest of those ...
708 // Remember that any unused bits in the final word are guaranteed to be unset.
709 for (auto i = words(); i--;) {
710 if (auto loc = highest_set_bit(word(i))) return i * bits_per_word + loc.value();
711 }
712 return {};
713 }
714
727 std::optional<usize> next_set(usize index) const {
728 // Start our search at index + 1.
729 index = index + 1;
730
731 // Perhaps we are off the end? (This also handles the case of an empty type).
732 if (index >= size()) return {};
733
734 // Where is that starting index located in the word store?
735 auto [word_index, bit] = index_and_offset<word_type>(index);
736
737 // Iterate forward looking for a word with a new set bit and use the lowest one ...
738 for (auto i = word_index; i < words(); ++i) {
739 auto w = word(i);
740 if (i == word_index) {
741 // First word -- turn all the bits before our starting bit into zeros so we don't see them as set.
742 reset_bits(w, 0, bit);
743 }
744 if (auto loc = lowest_set_bit(w)) return i * bits_per_word + loc.value();
745 }
746 return {};
747 }
748
761 std::optional<usize> previous_set(usize index) const {
762 // Edge case: If the store is empty or we are at the start, there are no previous set bits.
763 if (is_empty() || index == 0) return {};
764
765 // Silently fix large indices and also adjust the index down a slot.
766 if (index > size()) index = size();
767 index--;
768
769 // Where is that starting index located in the word store?
770 auto [word_index, bit] = index_and_offset<word_type>(index);
771
772 // Iterate backwards looking for a word with a new set bit and use the highest one ...
773 for (auto i = word_index + 1; i--;) {
774 auto w = word(i);
775 if (i == word_index) {
776 // First word -- turn all higher bits after our starting bit into zeros so we don't see them as set.
777 reset_bits(w, bit + 1, bits_per_word);
778 }
779 if (auto loc = highest_set_bit(w)) return i * bits_per_word + loc.value();
780 }
781 return {};
782 }
783
787
803 std::optional<usize> first_unset() const {
804 // Iterate forward looking for a word with a unset bit and use the lowest of those ...
805 for (auto i = 0uz; i < words(); ++i) {
806 auto w = word(i);
807 if (i == words() - 1) {
808 // Final word may have some unused zero bits that we need to replace with ones.
809 auto last_occupied_bit = bit_offset<word_type>(size() - 1);
810 set_bits(w, last_occupied_bit + 1, bits_per_word);
811 }
812 if (auto loc = lowest_unset_bit(w)) return i * bits_per_word + loc.value();
813 }
814 return {};
815 }
816
832 std::optional<usize> last_unset() const {
833 // Iterate backwards looking for a word with a unset bit and use the highest of those ...
834 for (auto i = words(); i--;) {
835 auto w = word(i);
836 if (i == words() - 1) {
837 // Final word may have some unused zero bits that we need to replace with ones.
838 auto last_occupied_bit = bit_offset<word_type>(size() - 1);
839 set_bits(w, last_occupied_bit + 1, bits_per_word);
840 }
841 if (auto loc = highest_unset_bit(w)) return i * bits_per_word + loc.value();
842 }
843 return {};
844 }
845
860 std::optional<usize> next_unset(usize index) const {
861 // Start our search at index + 1.
862 index = index + 1;
863
864 // Perhaps we are off the end? (This also handles the case of an empty type).
865 if (index >= size()) return {};
866
867 // Where is that starting index located in the word store?
868 auto [word_index, bit] = index_and_offset<word_type>(index);
869
870 // Iterate forward looking for a word with a new unset bit and use the lowest of those ...
871 for (auto i = word_index; i < words(); ++i) {
872 auto w = word(i);
873 if (i == word_index) {
874 // First word -- turn all the bits before our starting bit into ones so we don't see them as unset.
875 set_bits(w, 0, bit);
876 }
877 if (i == words() - 1) {
878 // Final word may have some unused zero bits that we need to replace with ones.
879 auto last_occupied_bit = bit_offset<word_type>(size() - 1);
880 set_bits(w, last_occupied_bit + 1, bits_per_word);
881 }
882 if (auto loc = lowest_unset_bit(w)) return i * bits_per_word + loc.value();
883 }
884 return {};
885 }
886
901 std::optional<usize> previous_unset(usize index) const {
902 // Edge case: If the store is empty or we are at the start, there are no previous set bits.
903 if (is_empty() || index == 0) return {};
904
905 // Silently fix large indices and also adjust the index down a slot.
906 if (index > size()) index = size();
907 index--;
908
909 // Where is that starting index located in the word store?
910 auto [word_index, bit] = index_and_offset<word_type>(index);
911
912 // Iterate backwards looking for a word with a new unset bit and use the highest of those ...
913 for (auto i = word_index + 1; i--;) {
914 auto w = word(i);
915 if (i == word_index) {
916 // First word -- set all the bits after our starting bit into ones so we don't see them as unset.
917 set_bits(w, bit + 1, bits_per_word);
918 }
919 if (i == words() - 1) {
920 // Final word may have some unused zero bits that we need to replace with ones.
921 auto last_occupied_bit = bit_offset<word_type>(size() - 1);
922 set_bits(w, last_occupied_bit + 1, bits_per_word);
923 }
924 if (auto loc = highest_unset_bit(w)) return i * bits_per_word + loc.value();
925 }
926 return {};
927 }
928
932
945 constexpr auto bits() const { return BitsIter<Store, true>{this, 0}; }
946
960 constexpr auto bits() { return BitsIter<Store, false>{this, 0}; }
961
973 constexpr auto set_bit_indices() const { return SetBitsIter{this}; }
974
986 constexpr auto unset_bit_indices() const { return UnsetBitsIter{this}; }
987
1005 constexpr auto store_words() const { return WordsIter{this}; }
1006
1017 constexpr auto to_words() const { return std::ranges::to<std::vector>(store_words()); }
1018
1022
1035 constexpr auto span(usize begin, usize end) const {
1036 // Check that the range is correctly formed and non-empty.
1037 gf2_assert(begin < end, "bit-span [{}, {}) is invalid.", begin, end);
1038
1039 // Check that the range does not extend beyond the end of the bit-store.
1040 gf2_assert(end <= size(), "bit-span end {} extends beyond the store end {}.", end, size());
1041
1042 // Get the zero-based word index and bit offset in that word for the begin location.
1043 auto [index, bit_offset] = index_and_offset<word_type>(begin);
1044
1045 // If the store is already a span over another store, we need to adjust those values.
1046 bit_offset += offset();
1047 if (bit_offset >= bits_per_word) {
1048 index += 1;
1050 }
1051
1052 // Return the bit-span over the underlying word data.
1053 return BitSpan<const word_type>(data() + index, bit_offset, end - begin);
1054 }
1055
1071 constexpr auto span(usize begin, usize end) {
1072 // Check that the range is correctly formed and non-empty.
1073 gf2_assert(begin < end, "bit-span [{}, {}) is invalid.", begin, end);
1074
1075 // Check that the range does not extend beyond the end of the bit-store.
1076 gf2_assert(end <= size(), "bit-span end {} extends beyond the store end {}.", end, size());
1077
1078 // Get the zero-based word index and bit offset in that word for the begin location.
1079 auto [index, bit_offset] = index_and_offset<word_type>(begin);
1080
1081 // If the store is already a span over another store, we need to adjust those values.
1082 bit_offset += offset();
1083 if (bit_offset >= bits_per_word) {
1084 index += 1;
1086 }
1087
1088 // Return the bit-span over the underlying word data.
1089 return BitSpan<word_type>(data() + index, bit_offset, end - begin);
1090 }
1091
1095
1109 constexpr auto sub(usize begin, usize end) const { return BitVec<word_type>::from(span(begin, end)); }
1110
1132 constexpr void split_at(usize at, BitVec<word_type>& left, BitVec<word_type>& right) const {
1133 gf2_assert(at <= size(), "split point {} is beyond the end of the bit-vector", at);
1134 left.clear();
1135 right.clear();
1136 left.append(span(0, at));
1137 right.append(span(at, size()));
1138 }
1139
1157 constexpr auto split_at(usize at) const {
1158 BitVec<word_type> left, right;
1159 split_at(at, left, right);
1160 return std::pair{left, right};
1161 }
1162
1166
1181 constexpr void riffle_into(BitVec<word_type>& dst) const {
1182 // Nothing to do if the bit-vector is empty or has just one element.
1183 // With two bits `ab` we fill `dst` with `a0b`.
1184 if (size() <= 1) {
1185 dst.copy(*this);
1186 return;
1187 }
1188
1189 // Make sure `dst` is large enough to hold the riffled bits (a bit too big but we fix that below).
1190 dst.resize(2 * size());
1191 auto dst_words = dst.words();
1192
1193 // Riffle each word in `this` into two adjacent words in `dst`.
1194 for (auto i = 0uz; i < words(); ++i) {
1195 auto [lo, hi] = riffle(word(i));
1196 dst.set_word(2 * i, lo);
1197
1198 // Note that `hi` may be completely superfluous ...
1199 if (2 * i + 1 < dst_words) dst.set_word(2 * i + 1, hi);
1200 }
1201
1202 // If this bit-store was say `abcde` then `dst` will now be `a0b0c0d0e0`. Pop the last 0.
1203 dst.pop();
1204 }
1205
1218 constexpr auto riffled() const {
1219 BitVec<word_type> result;
1220 riffle_into(result);
1221 return result;
1222 }
1223
1227
1241 constexpr void operator<<=(usize shift) {
1242 // Edge cases where there is nothing to do.
1243 if (shift == 0 || size() == 0) return;
1244
1245 // Perhaps we are shifting all the bits out and are left with all zeros.
1246 if (shift >= size()) {
1247 set_all(false);
1248 return;
1249 }
1250
1251 // For larger shifts, we can efficiently shift by whole words first.
1252 usize word_shift = shift / bits_per_word;
1253 usize end_word = words() - word_shift;
1254
1255 // Do the whole word shifts first, pushing in zero words to fill the empty slots.
1256 if (word_shift > 0) {
1257 // Shift whole words -- starting at the beginning of the store.
1258 for (auto i = 0uz; i < end_word; ++i) set_word(i, word(i + word_shift));
1259
1260 // Fill in the high order words with zeros.
1261 for (auto i = end_word; i < words(); ++i) set_word(i, 0);
1262
1263 // How many bits are left to shift?
1264 shift -= word_shift * bits_per_word;
1265 }
1266
1267 // Perhaps there are some partial word shifts left to do.
1268 if (shift != 0) {
1269 // Do the "interior" words where the shift moves bits from one word to the next.
1270 if (end_word > 0) {
1271 auto shift_complement = bits_per_word - shift;
1272 for (auto i = 0uz; i < end_word - 1; ++i) {
1273 auto lo = static_cast<word_type>(word(i) >> shift);
1274 auto hi = static_cast<word_type>(word(i + 1) << shift_complement);
1275 set_word(i, lo | hi);
1276 }
1277 }
1278
1279 // Do the last word.
1280 auto value = word(end_word - 1);
1281 set_word(end_word - 1, static_cast<word_type>(value >> shift));
1282 }
1283 }
1284
1298 constexpr void operator>>=(usize shift) {
1299 // Edge cases where there is nothing to do.
1300 if (shift == 0 || size() == 0) return;
1301
1302 // Perhaps we are shifting all the bits out and are left with all zeros.
1303 if (shift >= size()) {
1304 set_all(false);
1305 return;
1306 }
1307
1308 // For larger shifts, we can efficiently shift by whole words first.
1309 usize word_shift = shift / bits_per_word;
1310
1311 // Do the whole word shifts first, pushing in zero words to fill the empty slots.
1312 if (word_shift > 0) {
1313 // Shift whole words -- starting at the end of the store.
1314 for (auto i = words(); i-- > word_shift;) set_word(i, word(i - word_shift));
1315
1316 // Fill in the low order words with zeros.
1317 for (auto i = 0uz; i < word_shift; ++i) set_word(i, 0);
1318
1319 // How many bits are left to shift?
1320 shift -= word_shift * bits_per_word;
1321 }
1322
1323 // Perhaps there are some partial word shifts left to do.
1324 if (shift != 0) {
1325 // Do the "interior" words where the shift moves bits from one word to the next.
1326 auto shift_complement = bits_per_word - shift;
1327 for (auto i = words(); i-- > word_shift + 1;) {
1328 auto lo = static_cast<word_type>(word(i - 1) >> shift_complement);
1329 auto hi = static_cast<word_type>(word(i) << shift);
1330 set_word(i, lo | hi);
1331 }
1332 }
1333
1334 // Do the "first" word.
1335 auto value = word(word_shift);
1336 set_word(word_shift, static_cast<word_type>(value << shift));
1337 }
1338
1352 constexpr auto operator<<(usize shift) const {
1353 auto result = BitVec<word_type>::from(store());
1354 result <<= shift;
1355 return result;
1356 }
1357
1371 constexpr auto operator>>(usize shift) const {
1372 auto result = BitVec<word_type>::from(store());
1373 result >>= shift;
1374 return result;
1375 }
1376
1380
1391 template<typename Rhs>
1392 requires store_words_match<Store, Rhs>
1393 void operator^=(const BitStore<Rhs>& rhs) {
1394 gf2_assert(size() == rhs.size(), "Lengths do not match: {} != {}.", size(), rhs.size());
1395 for (auto i = 0uz; i < words(); ++i) set_word(i, word(i) ^ rhs.word(i));
1396 }
1397
1408 template<typename Rhs>
1409 requires store_words_match<Store, Rhs>
1410 void operator&=(const BitStore<Rhs>& rhs) {
1411 gf2_assert(size() == rhs.size(), "Lengths do not match: {} != {}.", size(), rhs.size());
1412 for (auto i = 0uz; i < words(); ++i) set_word(i, word(i) & rhs.word(i));
1413 }
1414
1425 template<typename Rhs>
1426 requires store_words_match<Store, Rhs>
1427 void operator|=(const BitStore<Rhs>& rhs) {
1428 gf2_assert(size() == rhs.size(), "Lengths do not match: {} != {}.", size(), rhs.size());
1429 for (auto i = 0uz; i < words(); ++i) set_word(i, word(i) | rhs.word(i));
1430 }
1431
1441 auto operator~() const {
1442 auto result = BitVec<word_type>::from(store());
1443 result.flip_all();
1444 return result;
1445 }
1446
1458 template<typename Rhs>
1459 requires store_words_match<Store, Rhs>
1460 auto operator^(const BitStore<Rhs>& rhs) const {
1461 auto result = BitVec<word_type>::from(store());
1462 result ^= rhs;
1463 return result;
1464 }
1465
1477 template<typename Rhs>
1478 requires store_words_match<Store, Rhs>
1479 auto operator&(const BitStore<Rhs>& rhs) const {
1480 auto result = BitVec<word_type>::from(store());
1481 result &= rhs;
1482 return result;
1483 }
1484
1496 template<typename Rhs>
1497 requires store_words_match<Store, Rhs>
1498 auto operator|(const BitStore<Rhs>& rhs) const {
1499 auto result = BitVec<word_type>::from(store());
1500 result |= rhs;
1501 return result;
1502 }
1503
1507
1520 template<typename Rhs>
1521 requires store_words_match<Store, Rhs>
1522 void operator+=(const BitStore<Rhs>& rhs) {
1523 operator^=(rhs);
1524 }
1525
1538 template<typename Rhs>
1539 requires store_words_match<Store, Rhs>
1540 void operator-=(const BitStore<Rhs>& rhs) {
1541 operator^=(rhs);
1542 }
1543
1557 template<typename Rhs>
1558 requires store_words_match<Store, Rhs>
1559 auto operator+(const BitStore<Rhs>& rhs) const {
1560 auto result = BitVec<word_type>::from(store());
1561 result += rhs;
1562 return result;
1563 }
1564
1578 template<typename Rhs>
1579 requires store_words_match<Store, Rhs>
1580 auto operator-(const BitStore<Rhs>& rhs) const {
1581 auto result = BitVec<word_type>::from(store());
1582 result -= rhs;
1583 return result;
1584 }
1585
1589
1606 inline std::string to_string(std::string_view sep = "", std::string_view pre = "",
1607 std::string_view post = "") const {
1608 return to_binary_string(sep, pre, post);
1609 }
1610
1623 inline std::string to_pretty_string() const { return to_binary_string(",", "[", "]"); }
1624
1641 std::string to_binary_string(std::string_view sep = "", std::string_view pre = "",
1642 std::string_view post = "") const {
1643 // Edge case ...
1644 if (is_empty()) { return std::format("{}{}", pre, post); }
1645
1646 // Start with the raw binary string by iterating over the words ...
1647 // We preallocate enough space to hold the entire string (actually this is usually a bit bigger than we need).
1648 auto num_words = words();
1649 std::string binary_string;
1650 binary_string.reserve(num_words * bits_per_word);
1651 for (auto i = 0uz; i < num_words; ++i) {
1652 auto w = reverse_bits(word(i));
1653 binary_string.append(gf2::to_binary_string(w));
1654 }
1655
1656 // The last word may not be fully occupied and we need to remove the spurious zeros
1657 binary_string.resize(size());
1658
1659 // If there is no separator, return early ...
1660 if (sep.empty()) { return std::format("{}{}{}", pre, binary_string, post); }
1661
1662 // Build `result` with separators between each character ...
1663 std::string result;
1664 result.reserve(pre.size() + size() * (sep.size() + 1) + post.size());
1665 result.append(pre);
1666 for (auto i = 0uz; i < size(); ++i) {
1667 result.push_back(binary_string[i]);
1668 if (i != size() - 1) { result.append(sep); }
1669 }
1670 result.append(post);
1671 return result;
1672 }
1673
1704 std::string to_hex_string() const {
1705 // Edge case: No bits in the store, return the empty string.
1706 if (is_empty()) return "";
1707
1708 // The number of digits in the output string. Generally hexadecimal but the last may be to a lower base.
1709 auto digits = (size() + 3) / 4;
1710
1711 // Preallocate space allowing for a possible lower base on the last digit such as "_2".
1712 std::string result;
1713 result.reserve(digits + 2);
1714
1715 // Reverse the bits in each word to vector-order and get the fully padded hex string representation.
1716 for (auto i = 0uz; i < words(); ++i) {
1717 auto w = reverse_bits(word(i));
1718 result.append(gf2::to_hex_string<word_type>(w));
1719 }
1720
1721 // Last word may not be fully occupied and padded with spurious zeros so we truncate the output string.
1722 result.resize(digits);
1723
1724 // Every four elements is encoded by a single hex digit but `size()` may not be a multiple of 4.
1725 auto k = size() % 4;
1726 if (k != 0) {
1727 // That last hex digit should really be encoded to a lower base -- 2, 4 or 8.
1728 // We compute the number represented by the trailing `k` elements in the bit-vector.
1729 int num = 0;
1730 for (auto i = 0uz; i < k; ++i)
1731 if (get(size() - 1 - i)) num |= 1 << i;
1732
1733 // Convert that number to hex & use it to *replace* the last hex digit in our `result` string.
1734 // Also append the appropriate base to the output string so that the last digit can be interpreted properly.
1735 result.resize(result.size() - 1);
1736 result.append(std::format("{:X}.{}", num, 1 << k));
1737 }
1738 return result;
1739 }
1740
1744 std::string describe() const {
1745 std::string result;
1746 result.reserve(1000);
1747 result += std::format("binary format: {}\n", to_binary_string());
1748 result += std::format("hex format: {}\n", to_hex_string());
1749 result += std::format("number of bits: {}\n", size());
1750 result += std::format("number of set bits: {}\n", count_ones());
1751 result += std::format("number of unset bits: {}\n", count_zeros());
1752 result += std::format("bits per word: {}\n", bits_per_word);
1753 result += std::format("word count: {}\n", words());
1754 auto words_vec = to_words();
1755 result += std::format("words in hex: {::#x}\n", words_vec);
1756 ;
1757 return result;
1758 }
1759
1760 // The usual output stream operator for a bit-store
1761 constexpr std::ostream& operator<<(std::ostream& s) const { return s << to_string(); }
1762};
1763
1764// --------------------------------------------------------------------------------------------------------------------
1765// Equality operator ...
1766// -------------------------------------------------------------------------------------------------------------------
1767
1772// # Example
1780template<typename Lhs, typename Rhs>
1781constexpr bool
1782operator==(const BitStore<Lhs>& lhs, const BitStore<Rhs>& rhs) {
1783 if constexpr (!store_words_match<Lhs, Rhs>) return false;
1784 if (&lhs != &rhs) {
1785 if (lhs.size() != rhs.size()) return false;
1786 for (auto i = 0uz; i < lhs.words(); ++i)
1787 if (lhs.word(i) != rhs.word(i)) return false;
1788 }
1789 return true;
1790}
1791
1792// --------------------------------------------------------------------------------------------------------------------
1793// Other functions ...
1794// -------------------------------------------------------------------------------------------------------------------
1795
1808template<typename Lhs, typename Rhs>
1809auto
1810join(const BitStore<Lhs>& lhs, const BitStore<Rhs>& rhs) {
1811 using word_type = typename store_word<Lhs>::word_type;
1812 auto result = BitVec<word_type>::from(lhs);
1813 result.append(rhs);
1814 return result;
1815}
1816
1832template<typename Lhs, typename Rhs>
1833 requires store_words_match<Lhs, Rhs>
1834constexpr bool
1835dot(const BitStore<Lhs>& lhs, const BitStore<Rhs>& rhs) {
1836 gf2_debug_assert_eq(lhs.size(), rhs.size(), "Length mismatch {} != {}", lhs.size(), rhs.size());
1837 using word_type = typename store_word<Lhs>::word_type;
1838 auto sum = word_type{0};
1839 for (auto i = 0uz; i < lhs.words(); ++i) { sum ^= static_cast<word_type>(lhs.word(i) & rhs.word(i)); }
1840 return count_ones(sum) % 2 == 1;
1841}
1842
1858template<typename Lhs, typename Rhs>
1859 requires store_words_match<Lhs, Rhs>
1860constexpr bool
1861operator*(const BitStore<Lhs>& lhs, const BitStore<Rhs>& rhs) {
1862 return dot(lhs, rhs);
1863}
1864
1877template<typename Lhs, typename Rhs>
1878 requires store_words_match<Lhs, Rhs>
1879auto
1880convolve(const BitStore<Lhs>& lhs, const BitStore<Rhs>& rhs) {
1881 using word_type = typename store_word<Lhs>::word_type;
1882
1883 // Edge case: if either store is empty then the convolution is empty.
1884 if (lhs.is_empty() || rhs.is_empty()) return BitVec<word_type>{};
1885
1886 // Generally the result will have length `self.size() + other.size() - 1` (could be all zeros).
1887 auto result = BitVec<word_type>::zeros(lhs.size() + rhs.size() - 1);
1888
1889 // If either vector is all zeros then the convolution is all zeros.
1890 if (lhs.none() || rhs.none()) return result;
1891
1892 // Only need to consider words in `rhs` up to and including the one holding its final set bit.
1893 // We have already checked that `rhs` is not all zeros so we know there is a last set bit!
1894 auto rhs_words_end = word_index<word_type>(rhs.last_set().value()) + 1;
1895
1896 // Initialize `result` by copying the live words from `rhs`
1897 for (auto i = 0uz; i < rhs_words_end; ++i) result.set_word(i, rhs.word(i));
1898
1899 // Work backwards from the last set bit in `lhs` (which we know exists as we checked `lhs` is not all zeros).
1900 for (auto i = lhs.last_set().value(); i-- > 0;) {
1901 word_type prev = 0;
1902 for (auto j = 0uz; j < result.words(); ++j) {
1903 auto left = static_cast<word_type>(prev >> (BITS<word_type> - 1));
1904 prev = result.word(j);
1905 result.set_word(j, static_cast<word_type>(prev << 1) | left);
1906 }
1907
1908 if (lhs.get(i)) {
1909 for (auto j = 0uz; j < rhs_words_end; ++j) result.set_word(j, result.word(j) ^ rhs.word(j));
1910 }
1911 }
1912 return result;
1913}
1914
1915} // namespace gf2
1916
1917// --------------------------------------------------------------------------------------------------------------------
1918// Specialise `std::formatter` to handle bit-stores ...
1919// -------------------------------------------------------------------------------------------------------------------
1920
1938template<typename Store>
1939struct std::formatter<gf2::BitStore<Store>> {
1940
1941 // Parse a bit-store format specifier where where we recognize {:p} and {:x}
1942 constexpr auto parse(const std::format_parse_context& ctx) {
1943 auto it = ctx.begin();
1944 while (it != ctx.end() && *it != '}') {
1945 switch (*it) {
1946 case 'p': m_pretty = true; break;
1947 case 'x': m_hex = true; break;
1948 default: m_error = true;
1949 }
1950 ++it;
1951 }
1952 return it;
1953 }
1954
1955 // Push out a formatted bit-vector using the various @c to_string(...) methods in the class.
1956 template<class FormatContext>
1957 auto format(const gf2::BitStore<Store>& rhs, FormatContext& ctx) const {
1958 // Was there a format specification error?
1959 if (m_error) return std::format_to(ctx.out(), "'UNRECOGNIZED FORMAT SPECIFIER FOR BIT-STORE'");
1960
1961 // Special handling requested?
1962 if (m_hex) return std::format_to(ctx.out(), "{}", rhs.to_hex_string());
1963 if (m_pretty) return std::format_to(ctx.out(), "{}", rhs.to_pretty_string());
1964
1965 // Default
1966 return std::format_to(ctx.out(), "{}", rhs.to_string());
1967 }
1968
1969 bool m_hex = false;
1970 bool m_pretty = false;
1971 bool m_error = false;
1972};
A BitRef is a proxy type that references a single bit in a BitStore. See the BitRef page for more d...
Five bit-store iterators that serve different purposes. See the Iterators page for more details.
Macros that improve on standard assert. See the Assertions page for all the details.
#define gf2_debug_assert(cond,...)
A confirmation macro that checks a boolean condition cond. On failure, the confirmation prints an e...
Definition assert.h:68
#define gf2_assert(cond,...)
A confirmation macro that checks a boolean condition cond. On failure, the confirmation prints an e...
Definition assert.h:98
#define gf2_debug_assert_eq(a, b,...)
A confirmation macro that checks the equality of two values lhs and rhs. On failure,...
Definition assert.h:83
A BitRef is a proxy class to reference a single bit in a bit-store.
Definition BitRef.h:38
A bit_span is a non-owning view of contiguous bits stored in some underlying store of unsigned Words....
Definition BitSpan.h:32
The base class for the vector-like types in the gf2 library: BitSet, BitVec, and BitSpan The templat...
Definition BitStore.h:35
constexpr auto split_at(usize at) const
Views a bit-store as two parts containing the elements [0, at) and [at, size()) respectively.
Definition BitStore.h:1157
constexpr usize words() const
Returns the fewest number of words needed to store the bits in the store.
Definition BitStore.h:69
std::string to_pretty_string() const
Returns a "pretty" string representation of the store.
Definition BitStore.h:1623
std::optional< usize > next_set(usize index) const
Returns the index of the next set bit after index in the store or {} if no more set bits exist.
Definition BitStore.h:727
auto operator+(const BitStore< Rhs > &rhs) const
Returns a new bit-vector that is the + of this store with the passed (equal-sized) rhs bit-store.
Definition BitStore.h:1559
constexpr auto to_words() const
Returns a copy of the words underlying this bit-store.
Definition BitStore.h:1017
constexpr word_type word(usize i) const
Returns "word" i underpinning this bit store.
Definition BitStore.h:81
auto set_all(bool value=true)
Sets the bits in the store to the boolean value and returns a reference to this for chaining.
Definition BitStore.h:371
constexpr auto operator<<(usize shift) const
Returns a new bit-vector that is lhs shifted left by shift bits.
Definition BitStore.h:1352
auto operator&(const BitStore< Rhs > &rhs) const
Returns a new bit-vector that is the AND of the current bit-store and another equal-sized bit-store.
Definition BitStore.h:1479
constexpr bool all() const
Returns true if all bits in the store are set, false otherwise.
Definition BitStore.h:327
auto copy(const BitStore< SrcStore > &src)
Copies the bits from an equal-sized src store and returns a reference to this for chaining.
Definition BitStore.h:446
void operator+=(const BitStore< Rhs > &rhs)
In-place addition of this bit-store with the an equal-sized rhs bit-store.
Definition BitStore.h:1522
static constexpr u8 bits_per_word
The number of bits in each store word.
Definition BitStore.h:48
constexpr auto operator>>(usize shift) const
Returns a new bit-vector that is lhs shifted right by shift bits.
Definition BitStore.h:1371
std::optional< usize > first_set() const
Returns the index of the first set bit in the bit-store or {} if no bits are set.
Definition BitStore.h:684
constexpr bool is_empty() const
Returns true if the store is empty, false otherwise.
Definition BitStore.h:295
constexpr auto span(usize begin, usize end) const
Returns an immutable bit-span encompassing the bits in the half-open range [begin,...
Definition BitStore.h:1035
constexpr auto bits() const
Returns a const iterator over the bool values of the bits in the const bit-store.
Definition BitStore.h:945
std::string to_binary_string(std::string_view sep="", std::string_view pre="", std::string_view post="") const
Returns a binary string representation of the store.
Definition BitStore.h:1641
constexpr usize count_ones() const
Returns the number of set bits in the store.
Definition BitStore.h:601
auto operator|(const BitStore< Rhs > &rhs) const
Returns a new bit-vector that is the OR of the current bit-store and another equal-sized bit-store.
Definition BitStore.h:1498
constexpr usize leading_zeros() const
Returns the number of leading zeros in the store.
Definition BitStore.h:629
auto fill(std::invocable< usize > auto f)
Fill the store by repeatedly calling f(i) and returns a reference to this for chaining.
Definition BitStore.h:536
void operator|=(const BitStore< Rhs > &rhs)
In-place OR with another equal-sized bit-store.
Definition BitStore.h:1427
constexpr bool get(usize i) const
Returns true if the bit at the given index i is set, false otherwise.
Definition BitStore.h:123
constexpr auto riffled() const
Returns a new bit-vector that is the result of riffling the bits in this bit-store with zeros.
Definition BitStore.h:1218
std::optional< usize > previous_unset(usize index) const
Returns the index of the previous unset bit before index in the store or {} if no more unset bits exi...
Definition BitStore.h:901
constexpr auto operator[](usize index)
Returns a "reference" to the bit element at index.
Definition BitStore.h:217
constexpr u8 offset() const
Returns the offset (in bits) from the bit 0 of data()[0] to the first bit in the store.
Definition BitStore.h:106
void operator^=(const BitStore< Rhs > &rhs)
In-place XOR with another equal-sized bit-store.
Definition BitStore.h:1393
constexpr auto store_words() const
Returns a const iterator over all the words underlying the bit-store.
Definition BitStore.h:1005
void operator-=(const BitStore< Rhs > &rhs)
In-place subtraction of this bit-store with the an equal-sized rhs bit-store.
Definition BitStore.h:1540
constexpr usize size() const
Returns the number of bit elements in the store.
Definition BitStore.h:63
auto set(usize index, bool value=true)
Sets the bit-element at the given index to the specified boolean value & returns this for chaining....
Definition BitStore.h:191
std::optional< usize > last_set() const
Returns the index of the last set bit in the bit-store or {} if no bits are set.
Definition BitStore.h:706
std::optional< usize > next_unset(usize index) const
Returns the index of the next unset bit after index in the store or {} if no more unset bits exist.
Definition BitStore.h:860
constexpr const word_type * data() const
Returns a const pointer to the real first word in the underlying store.
Definition BitStore.h:96
std::optional< usize > first_unset() const
Returns the index of the first unset bit in the bit-store or {} if no bits are unset.
Definition BitStore.h:803
std::string to_string(std::string_view sep="", std::string_view pre="", std::string_view post="") const
Returns a binary string representation of the store.
Definition BitStore.h:1606
constexpr auto sub(usize begin, usize end) const
Returns a clone of the elements in the half-open range [begin, end) as a new bit-vector.
Definition BitStore.h:1109
constexpr bool front() const
Returns true if the first bit element is set, false otherwise.
Definition BitStore.h:154
constexpr void set_word(usize i, word_type value)
This method sets "word" at index i to the specified value.
Definition BitStore.h:93
void operator&=(const BitStore< Rhs > &rhs)
In-place AND with another equal-sized bit-store.
Definition BitStore.h:1410
constexpr bool none() const
Returns true if no bits in the store are set, false otherwise.
Definition BitStore.h:355
auto flip(usize index)
Flips the value of the bit-element at the given index and returns this for chaining/.
Definition BitStore.h:233
constexpr void split_at(usize at, BitVec< word_type > &left, BitVec< word_type > &right) const
Views a bit-store as two parts containing the elements [0, at) and [at, size()) respectively.
Definition BitStore.h:1132
constexpr void operator<<=(usize shift)
In-place left shift of the bit-store by shift bits.
Definition BitStore.h:1241
auto operator^(const BitStore< Rhs > &rhs) const
Returns a new bit-vector that is the XOR of the current bit-store and another equal-sized bit-store.
Definition BitStore.h:1460
typename store_word< Store >::word_type word_type
Unsigned word type used to store the bits (u8, 16, u32, u64, or usize).
Definition BitStore.h:45
std::optional< usize > previous_set(usize index) const
Returns the index of the previous set bit before index in the store or {} if there are none.
Definition BitStore.h:761
constexpr auto unset_bit_indices() const
Returns an iterator over the indices of any unset bits in the bit-store.
Definition BitStore.h:986
constexpr auto swap(usize i0, usize i1)
Swaps the bits in the bit-store at indices i0 and i1 and returns this for chaining.
Definition BitStore.h:258
constexpr auto bits()
Returns a non-const iterator over the values of the bits in the mutable bit-store.
Definition BitStore.h:960
constexpr word_type * data()
Returns a pointer to the real first word in the underlying store.
Definition BitStore.h:101
constexpr bool any() const
Returns true if at least one bit in the store is set, false otherwise.
Definition BitStore.h:308
constexpr usize count_zeros() const
Returns the number of unset bits in the store.
Definition BitStore.h:616
std::optional< usize > last_unset() const
Returns the index of the last unset bit in the bit-store or {} if no bits are unset.
Definition BitStore.h:832
constexpr void operator>>=(usize shift)
In-place right shift of the bit-store by shift bits.
Definition BitStore.h:1298
auto copy(Src src)
Copies the bits from an unsigned integral src value and returns a reference to this for chaining.
Definition BitStore.h:412
auto operator-(const BitStore< Rhs > &rhs) const
Returns a new bit-vector that is the - of this store with the passed (equal-sized) rhs bit-store.
Definition BitStore.h:1580
auto copy(const std::bitset< N > &src)
Copies the bits of an equal-sized std::bitset and returns a reference to this for chaining.
Definition BitStore.h:516
std::string describe() const
Returns a multi-line string describing the bit-vector in some detail.
Definition BitStore.h:1744
constexpr bool operator[](usize index) const
Returns the boolean value of the bit element at index.
Definition BitStore.h:141
auto flip_all()
Flips the value of the bits in the store and returns a reference to this for chaining.
Definition BitStore.h:385
constexpr auto span(usize begin, usize end)
Returns a mutable bit-span encompassing the bits in the half-open range [begin, end).
Definition BitStore.h:1071
constexpr usize trailing_zeros() const
Returns the number of trailing zeros in the store.
Definition BitStore.h:647
auto operator~() const
Returns a new bit-vector that has the same bits as the current bit-store but with all the bits flippe...
Definition BitStore.h:1441
auto random_fill(double p=0.5, u64 seed=0)
Fill the store with random bits and returns a reference to this for chaining.
Definition BitStore.h:560
constexpr auto set_bit_indices() const
Returns an iterator over the indices of any set bits in the bit-store.
Definition BitStore.h:973
std::string to_hex_string() const
Returns the "hex" string representation of the bits in the bit-store.
Definition BitStore.h:1704
constexpr bool back() const
Returns true if the last bit element is set, false otherwise.
Definition BitStore.h:170
constexpr void riffle_into(BitVec< word_type > &dst) const
Interleaves the bits of this bit-store with zeros storing the result into the bit-vector dst.
Definition BitStore.h:1181
A dynamically-sized vector over GF(2) with bit elements compactly stored in a standard vector of prim...
Definition BitVec.h:37
constexpr std::optional< bool > pop()
Removes the last bit from the bit-vector and returns it or std::nullopt if the bit-vector is empty.
Definition BitVec.h:767
constexpr usize words() const
Returns the number of words in the bit-vector's underlying word store.
Definition BitVec.h:77
static constexpr BitVec zeros(usize n)
Factory method to generate a bit-vector of length n where the elements are all 0.
Definition BitVec.h:212
static constexpr BitVec from(const BitStore< Src > &src)
Factory method to construct a bit-vector by copying all the bits from any BitStore instance.
Definition BitVec.h:277
constexpr BitVec & resize(usize n)
Resize the bit-vector so that its size() is n.
Definition BitVec.h:576
constexpr BitVec & clear()
Removes all elements from the bit-vector so size()==0.
Definition BitVec.h:557
constexpr void set_word(usize i, Word word)
Sets word i in the bit-vector's underlying word store to value (masked if necessary).
Definition BitVec.h:112
auto append(Src src)
Appends all the bits from any unsigned integral src value and returns a reference to this for chainin...
Definition BitVec.h:634
Two iterators over all the bits in a bit-store — one const and the other non-const....
Definition Iterators.h:45
An iterator over the index locations of the set bits in a bit-store. You get this iterator by calli...
Definition Iterators.h:130
An iterator over the index locations of the unset bits in a bit-store. You get this iterator by call...
Definition Iterators.h:212
An iterator over the "words" underlying a bit-store. You get this iterator by calling the BitStore::...
Definition Iterators.h:295
The namespace for the gf2 library.
Definition assert.h:119
constexpr std::pair< Word, Word > riffle(Word word)
Riffles an unsigned_word into a pair of others containing the bits in the original word interleaved w...
Definition unsigned_word.h:294
constexpr std::pair< usize, u8 > index_and_offset(usize i)
Returns a pair of the index of the word and the bit position within the word for bit element i.
Definition unsigned_word.h:572
constexpr u8 bit_offset(usize i)
Returns the bit position within the containing word for bit element i.
Definition unsigned_word.h:555
constexpr void set_bits(Word &word, u8 begin, u8 end)
Sets the bits in the half-open range [begin, end) of word to one, leaving the others unchanged.
Definition unsigned_word.h:153
constexpr bool operator==(const BitStore< Lhs > &lhs, const BitStore< Rhs > &rhs)
Checks that any pair of bit-stores are equal in content.
Definition BitStore.h:1782
constexpr Word reverse_bits(Word word)
Returns a copy of word with all its bits reversed.
Definition unsigned_word.h:242
constexpr auto dot(const BitMat< Word > &lhs, const BitStore< Rhs > &rhs)
Bit-matrix, bit-store multiplication, M * v, returning a new bit-vector.
Definition BitMat.h:2514
auto join(const BitStore< Lhs > &lhs, const BitStore< Rhs > &rhs)
Returns a new bit-vector formed by joining two bit-stores lhs and rhs.
Definition BitStore.h:1810
constexpr u8 leading_zeros(Word word)
Returns the number of leading zeros in an unsigned_word.
Definition unsigned_word.h:372
std::uint64_t u64
Word type alias for a 64-bit unsigned integer.
Definition unsigned_word.h:38
constexpr void reset_bits(Word &word, u8 begin, u8 end)
Resets the bits in the half-open range [begin, end) of word to zero, leaving the others unchanged.
Definition unsigned_word.h:171
constexpr auto operator*(const BitMat< Word > &lhs, const BitStore< Rhs > &rhs)
Operator form for bit-matrix, bit-vector multiplication, M * v, returning a new bit-vector.
Definition BitMat.h:2526
std::uint8_t u8
Word type alias for an 8-bit unsigned integer.
Definition unsigned_word.h:29
static std::string to_binary_string(Word word)
Returns the binary string representation of an unsigned_word showing all the bits.
Definition unsigned_word.h:489
auto convolve(const BitStore< Lhs > &lhs, const BitStore< Rhs > &rhs)
Returns the convolution of this store with the given rhs store as a new bit-vector.
Definition BitStore.h:1880
constexpr usize word_index(usize i)
Returns the index of the unsigned_word word holding bit element i.
Definition unsigned_word.h:539
constexpr std::pair< usize, Word > index_and_mask(usize i)
Returns a pair of the index of the word and a mask isolating bit element i.
Definition unsigned_word.h:589
constexpr u8 count_ones(Word word)
Returns the number of set bits in an unsigned_word.
Definition unsigned_word.h:327
constexpr u8 trailing_zeros(Word word)
Returns the number of trailing zeros in an unsigned_word.
Definition unsigned_word.h:357
std::size_t usize
Word type alias for the platform's "native"-sized unsigned integer.
Definition unsigned_word.h:41
constexpr std::optional< u8 > lowest_unset_bit(Word word)
Returns the index of the lowest unset bit in an unsigned_word or std::nullopt if there are no unset b...
Definition unsigned_word.h:453
constexpr std::optional< u8 > highest_set_bit(Word word)
Returns the index of the highest set bit in an unsigned_word or std::nullopt if no bits are set.
Definition unsigned_word.h:437
constexpr Word MAX
The unsigned_word instance with all its bits set to 1.
Definition unsigned_word.h:81
constexpr u8 BITS
The number of bits in an unsigned_word returned as a u8.
Definition unsigned_word.h:54
constexpr std::optional< u8 > lowest_set_bit(Word word)
Returns the index of the lowest set bit in an unsigned_word or std::nullopt if no bits are set.
Definition unsigned_word.h:417
static std::string to_hex_string(Word word)
Returns the hex string representation of an unsigned_word showing all the bits.
Definition unsigned_word.h:504
constexpr std::optional< u8 > highest_unset_bit(Word word)
Returns the index of the highest unset bit in an unsigned_word or std::nullopt if there are none.
Definition unsigned_word.h:469
Utility functions that work on primitive unsigned word types. See the unsigned word page for more d...