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
9
10#include <gf2/assert.h>
11#include <gf2/Unsigned.h>
12#include <gf2/RNG.h>
13
14#include <algorithm>
15#include <bitset>
16#include <concepts>
17#include <format>
18#include <optional>
19#include <ranges>
20#include <string>
21#include <string_view>
22#include <utility>
23#include <vector>
24#include <type_traits>
25
26// --------------------------------------------------------------------------------------------------------------------
27// The BitStore concept ...
28// --------------------------------------------------------------------------------------------------------------------
29
30namespace gf2 {
31
69template<typename Store>
70concept BitStore = requires(Store store, const Store const_store) {
72 typename Store::word_type;
74
76 { const_store.size() } -> std::same_as<usize>;
77
79 { const_store.store() } -> std::same_as<const typename Store::word_type*>;
80
83 { store.store() } -> std::convertible_to<const typename Store::word_type*>;
84
87 { const_store.offset() } -> std::same_as<u8>;
88
93 { const_store.words() } -> std::same_as<usize>;
94
105 { const_store.word(usize{}) } -> std::same_as<typename Store::word_type>;
106
118 { store.set_word(usize{}, typename Store::word_type{}) } -> std::same_as<void>;
119};
120
121} // namespace gf2
122
123// --------------------------------------------------------------------------------------------------------------------
124// Forward Declarations of BitStore Related Types.
125// --------------------------------------------------------------------------------------------------------------------
126
127namespace gf2 {
128
129// clang-format off
130template<usize N, Unsigned Word> class BitSet;
131template<Unsigned Word> class BitVec;
132template<Unsigned Word> class BitSpan;
133template<BitStore Store> class BitRef;
134template<BitStore Store, bool is_const> class BitsIter;
135template<BitStore Store> class SetBitsIter;
136template<BitStore Store> class UnsetBitsIter;
137template<BitStore Store> class WordsIter;
138// clang-format on
139
140} // namespace gf2
141
142// --------------------------------------------------------------------------------------------------------------------
143// Free Functions for BitStore Types.
144// --------------------------------------------------------------------------------------------------------------------
145
146namespace gf2 {
149
161template<BitStore Store>
162constexpr bool
163get(Store const& store, usize i) {
164 gf2_debug_assert(i < store.size(), "Index {} is out of bounds for store of length {}", i, store.size());
166 return store.word(word_index) & word_mask;
167}
168
187template<BitStore Store>
188constexpr auto
189ref(Store& store, usize i) {
190 gf2_debug_assert(i < store.size(), "Index {} is out of bounds for store of length {}", i, store.size());
191 return BitRef{&store, i};
192}
193
205template<BitStore Store>
206constexpr bool
207front(Store const& store) {
208 gf2_debug_assert(!is_empty(store), "The store is empty!");
209 return get(store, 0);
210}
211
223template<BitStore Store>
224constexpr bool
225back(Store const& store) {
226 gf2_debug_assert(!is_empty(store), "The store is empty!");
227 return get(store, store.size() - 1);
228}
229
242template<BitStore Store>
243constexpr auto&
244set(Store& store, usize i, bool value = true) {
245 gf2_debug_assert(i < store.size(), "Index {} is out of bounds for store of length {}", i, store.size());
247 auto word_value = store.word(word_index);
248 auto bit_value = (word_value & word_mask) != 0;
249 if (bit_value != value) store.set_word(word_index, word_value ^ word_mask);
250 return store;
251}
252
267template<BitStore Store>
268constexpr auto&
269flip(Store& store, usize i) {
270 gf2_debug_assert(i < store.size(), "Index {} is out of bounds for store of length {}", i, store.size());
272 auto word_value = store.word(word_index);
273 store.set_word(word_index, word_value ^ word_mask);
274 return store;
275}
276
295template<BitStore Store>
296constexpr auto&
297swap(Store& store, usize i0, usize i1) {
298 auto sz = store.size();
299 gf2_debug_assert(i0 < sz, "index {} is out of bounds for a store of length {}", i0, sz);
300 gf2_debug_assert(i1 < sz, "index {} is out of bounds for a store of length {}", i1, sz);
301 if (i0 != i1) {
302 auto [w0, mask0] = index_and_mask<typename Store::word_type>(i0);
303 auto [w1, mask1] = index_and_mask<typename Store::word_type>(i1);
304 auto word0 = store.word(w0);
305 auto word1 = store.word(w1);
306 auto val0 = (word0 & mask0) != 0;
307 auto val1 = (word1 & mask1) != 0;
308 if (val0 != val1) {
309 if (w0 == w1) {
310 // Both bits are in the same word
311 store.set_word(w0, word0 ^ mask0 ^ mask1);
312 } else {
313 // The two bits are in different words
314 store.set_word(w0, word0 ^ mask0);
315 store.set_word(w1, word1 ^ mask1);
316 }
317 }
318 }
319 return store;
320}
321
325
335template<BitStore Store>
336constexpr bool
337is_empty(Store const& store) {
338 return store.size() == 0;
339}
340
352template<BitStore Store>
353constexpr bool
354any(Store const& store) {
355 for (auto i = 0uz; i < store.words(); ++i)
356 if (store.word(i) != 0) return true;
357 return false;
358}
359
373template<BitStore Store>
374constexpr bool
375all(Store const& store) {
376 auto num_words = store.words();
377 if (num_words == 0) { return true; }
378
379 // Check the fully occupied words ...
380 using word_type = typename Store::word_type;
381 for (auto i = 0uz; i < num_words - 1; ++i) {
382 if (store.word(i) != MAX<word_type>) return false;
383 }
384
385 // Last word might not be fully occupied ...
386 auto bits_per_word = BITS<word_type>;
387 auto tail_bits = store.size() % bits_per_word;
388 auto unused_bits = tail_bits == 0 ? 0 : bits_per_word - tail_bits;
389 auto last_word_max = MAX<word_type> >> unused_bits;
390 if (store.word(num_words - 1) != last_word_max) return false;
391
392 return true;
393}
394
406template<BitStore Store>
407constexpr bool
408none(Store const& store) {
409 return !any(store);
410}
411
415
426template<BitStore Store>
427constexpr auto&
428set_all(Store& store, bool value = true) {
429 using word_type = typename Store::word_type;
430 auto word_value = value ? MAX<word_type> : word_type{0};
431 for (auto i = 0uz; i < store.words(); ++i) store.set_word(i, word_value);
432 return store;
433}
434
443template<BitStore Store>
444constexpr auto&
445flip_all(Store& store) {
446 using word_type = typename Store::word_type;
447 for (auto i = 0uz; i < store.words(); ++i) store.set_word(i, static_cast<word_type>(~store.word(i)));
448 return store;
449}
450
454
472template<Unsigned Src, BitStore Store>
473constexpr auto&
474copy(Src src, Store& store) {
475 constexpr auto src_bits = BITS<Src>;
476 gf2_assert(store.size() == src_bits, "Lengths do not match: {} != {}.", store.size(), src_bits);
477
478 using word_type = typename Store::word_type;
479 constexpr auto bits_per_word = BITS<word_type>;
480 if constexpr (src_bits <= bits_per_word) {
481 // The source fits into a single store word
482 store.set_word(0, static_cast<word_type>(src));
483 } else {
484 // The source spans some number of store words (which we assume is an integer number).
485 constexpr auto num_words = src_bits / bits_per_word;
486 for (auto i = 0uz; i < num_words; ++i) {
487 auto shift = i * bits_per_word;
488 auto word_value = static_cast<word_type>((src >> shift) & MAX<word_type>);
489 store.set_word(i, word_value);
490 }
491 }
492 return store;
493}
494
509template<BitStore Src, BitStore Store>
510constexpr auto&
511copy(Src const& src, Store& store) {
512 // The sizes must match ...
513 gf2_assert(store.size() == src.size(), "Lengths do not match: {} != {}.", store.size(), src.size());
514
515 // What is the word types (without any const qualifiers)?
516 using word_type = std::remove_const_t<typename Store::word_type>;
517 using src_word_type = std::remove_const_t<typename Src::word_type>;
518
519 // Numbers of bits per respective words
520 constexpr auto word_bits = BITS<word_type>;
521 constexpr auto src_word_bits = BITS<src_word_type>;
522
523 // What we do next depends on the size of the respective words.
524 if constexpr (word_bits == src_word_bits) {
525 // Easiest case: Same sized words -- we can copy whole words directly ...
526 for (auto i = 0uz; i < store.words(); ++i) {
527 auto src_word = static_cast<word_type>(src.word(i));
528 store.set_word(i, src_word);
529 }
530 } else if constexpr (word_bits > src_word_bits) {
531 // Our word type is larger -- some number of source words fit into each store word.
532 // We assume that our word size is an integer multiple of the source word size.
533 constexpr auto ratio = word_bits / src_word_bits;
534
535 auto src_words = src.words();
536 for (auto i = 0uz; i < store.words(); ++i) {
537
538 // Combine `ratio` source words into a single destination word being careful not to run off the end ...
539 word_type dst_word = 0;
540 for (auto j = 0uz; j < ratio; ++j) {
541 auto src_index = i * ratio + j;
542 word_type src_word = 0;
543 if (src_index < src_words) src_word = static_cast<word_type>(src.word(src_index));
544 dst_word |= (src_word << (j * src_word_bits));
545 }
546 store.set_word(i, dst_word);
547 }
548 } else {
549 // The store word size is smaller -- each source word becomes multiple destination words.
550 // We assume that the source word size is an integer multiple of our word size.
551 constexpr auto ratio = src_word_bits / word_bits;
552
553 auto dst_words = store.words();
554 for (auto src_index = 0uz; src_index < src.words(); ++src_index) {
555 auto src_word = src.word(src_index);
556
557 // Split the source word into `ratio` destination words ...
558 for (auto j = 0uz; j < ratio; ++j) {
559 auto dst_index = src_index * ratio + j;
560 if (dst_index >= dst_words) break;
561 auto shift = j * word_bits;
562 auto dst_word = static_cast<word_type>((src_word >> shift) & MAX<word_type>);
563 store.set_word(dst_index, dst_word);
564 }
565 }
566 }
567 return store;
568}
569
581template<usize N, BitStore Store>
582constexpr auto&
583copy(std::bitset<N> const& src, Store& store) {
584 // The sizes must match
585 gf2_assert(store.size() == N, "Lengths do not match: {} != {}.", store.size(), N);
586
587 // Doing this the dumb way as there is no standard access to the bitset's underlying store
588 set_all(store, false);
589 for (auto i = 0uz; i < N; ++i)
590 if (src[i]) set(store, i);
591 return store;
592}
593
603template<BitStore Store>
604constexpr auto&
605copy(Store& store, std::invocable<usize> auto f) {
606 set_all(store, false);
607 for (auto i = 0uz; i < store.size(); ++i)
608 if (f(i) == true) set(store, i);
609 return store;
610}
611
629template<BitStore Store>
630constexpr auto&
631random_fill(Store& store, double p = 0.5, u64 seed = 0) {
632 // Keep a single static RNG per thread for all calls to this method, seeded with entropy on the first call.
633 thread_local RNG rng;
634
635 // Edge case handling ...
636 if (p < 0) return set_all(store, false);
637
638 // Scale p by 2^64 to remove floating point arithmetic from the main loop below.
639 // If we determine p rounds to 1 then we can just set all elements to 1 and return early.
640 p = p * 0x1p64 + 0.5;
641 if (p >= 0x1p64) return set_all(store);
642
643 // p does not round to 1 so we use a 64-bit URNG and check each draw against the 64-bit scaled p.
644 auto scaled_p = static_cast<u64>(p);
645
646 // If a seed was provided, set the RNG's seed to it. Otherwise, we carry on from where we left off.
647 u64 old_seed = rng.seed();
648 if (seed != 0) rng.set_seed(seed);
649
650 set_all(store, false);
651 for (auto i = 0uz; i < store.size(); ++i)
652 if (rng.u64() < scaled_p) set(store, i);
653
654 // Restore the old seed if necessary.
655 if (seed != 0) rng.set_seed(old_seed);
656 return store;
657}
658
662
672template<BitStore Store>
673constexpr usize
674count_ones(Store const& store) {
675 usize count = 0;
676 for (auto i = 0uz; i < store.words(); ++i) count += static_cast<usize>(gf2::count_ones(store.word(i)));
677 return count;
678}
679
689template<BitStore Store>
690constexpr usize
691count_zeros(Store const& store) {
692 return store.size() - count_ones(store);
693}
694
706template<BitStore Store>
707constexpr usize
708leading_zeros(Store const& store) {
709 // Note: Even if the last word is not fully occupied, we know unused bits are set to 0.
710 auto bits_per_word = BITS<typename Store::word_type>;
711 for (auto i = 0uz; i < store.words(); ++i) {
712 auto w = store.word(i);
713 if (w != 0) return i * bits_per_word + static_cast<usize>(gf2::trailing_zeros(w));
714 }
715 return store.size();
716}
717
727template<BitStore Store>
728constexpr usize
729trailing_zeros(Store const& store) {
730 if (is_empty(store)) return 0;
731
732 // The last word may not be fully occupied so we need to subtract the number of unused bits.
733 auto bits_per_word = BITS<typename Store::word_type>;
734 auto tail_bits = store.size() % bits_per_word;
735 auto unused_bits = tail_bits == 0 ? 0 : bits_per_word - tail_bits;
736 auto num_words = store.words();
737 auto i = num_words;
738 while (i--) {
739 auto w = store.word(i);
740 if (w != 0) {
741 auto whole_count = (num_words - i - 1) * bits_per_word;
742 auto partial_count = static_cast<usize>(gf2::leading_zeros(w)) - unused_bits;
743 return whole_count + partial_count;
744 }
745 }
746 return store.size();
747}
748
752
768template<BitStore Store>
769constexpr std::optional<usize>
770first_set(Store const& store) {
771 // Iterate forward looking for a word with a set bit and use the lowest of those ...
772 // Remember that any unused bits in the final word are guaranteed to be unset.
773 auto bits_per_word = BITS<typename Store::word_type>;
774 for (auto i = 0uz; i < store.words(); ++i) {
775 if (auto loc = lowest_set_bit(store.word(i))) return i * bits_per_word + loc.value();
776 }
777 return {};
778}
779
793template<BitStore Store>
794constexpr std::optional<usize>
795last_set(Store const& store) {
796 // Iterate backward looking for a word with a set bit and use the highest of those ...
797 // Remember that any unused bits in the final word are guaranteed to be unset.
798 auto bits_per_word = BITS<typename Store::word_type>;
799 for (auto i = store.words(); i--;) {
800 if (auto loc = highest_set_bit(store.word(i))) return i * bits_per_word + loc.value();
801 }
802 return {};
803}
804
817template<BitStore Store>
818constexpr std::optional<usize>
819next_set(Store const& store, usize index) {
820 // Start our search at index + 1.
821 index = index + 1;
822
823 // Perhaps we are off the end? (This also handles the case of an empty type).
824 if (index >= store.size()) return {};
825
826 // Where is that starting index located in the word store?
827 using word_type = typename Store::word_type;
828 auto bits_per_word = BITS<word_type>;
829 auto [word_index, bit] = index_and_offset<word_type>(index);
830
831 // Iterate forward looking for a word with a new set bit and use the lowest one ...
832 for (auto i = word_index; i < store.words(); ++i) {
833 auto w = store.word(i);
834 if (i == word_index) {
835 // First word -- turn all the bits before our starting bit into zeros so we don't see them as set.
836 reset_bits(w, 0, bit);
837 }
838 if (auto loc = lowest_set_bit(w)) return i * bits_per_word + loc.value();
839 }
840 return {};
841}
842
855template<BitStore Store>
856constexpr std::optional<usize>
857previous_set(Store const& store, usize index) {
858 // Edge case: If the store is empty or we are at the start, there are no previous set bits.
859 if (is_empty(store) || index == 0) return {};
860
861 // Silently fix large indices and also adjust the index down a slot.
862 if (index > store.size()) index = store.size();
863 index--;
864
865 // Where is that starting index located in the word store?
866 using word_type = typename Store::word_type;
867 auto bits_per_word = BITS<word_type>;
868 auto [word_index, bit] = index_and_offset<word_type>(index);
869
870 // Iterate backwards looking for a word with a new set bit and use the highest one ...
871 for (auto i = word_index + 1; i--;) {
872 auto w = store.word(i);
873 if (i == word_index) {
874 // First word -- turn all higher bits after our starting bit into zeros so we don't see them as set.
875 reset_bits(w, bit + 1, bits_per_word);
876 }
877 if (auto loc = highest_set_bit(w)) return i * bits_per_word + loc.value();
878 }
879 return {};
880}
881
885
901template<BitStore Store>
902constexpr std::optional<usize>
903first_unset(Store const& store) {
904 // Iterate forward looking for a word with a unset bit and use the lowest of those ...
905 using word_type = typename Store::word_type;
906 auto bits_per_word = BITS<word_type>;
907 for (auto i = 0uz; i < store.words(); ++i) {
908 auto w = store.word(i);
909 if (i == store.words() - 1) {
910 // Final word may have some unused zero bits that we need to replace with ones.
911 auto last_occupied_bit = bit_offset<word_type>(store.size() - 1);
912 set_bits(w, last_occupied_bit + 1, bits_per_word);
913 }
914 if (auto loc = lowest_unset_bit(w)) return i * bits_per_word + loc.value();
915 }
916 return {};
917}
918
934template<BitStore Store>
935constexpr std::optional<usize>
936last_unset(Store const& store) {
937 // Iterate backwards looking for a word with a unset bit and use the highest of those ...
938 using word_type = typename Store::word_type;
939 auto bits_per_word = BITS<word_type>;
940 for (auto i = store.words(); i--;) {
941 auto w = store.word(i);
942 if (i == store.words() - 1) {
943 // Final word may have some unused zero bits that we need to replace with ones.
944 auto last_occupied_bit = bit_offset<word_type>(store.size() - 1);
945 set_bits(w, last_occupied_bit + 1, bits_per_word);
946 }
947 if (auto loc = highest_unset_bit(w)) return i * bits_per_word + loc.value();
948 }
949 return {};
950}
951
966template<BitStore Store>
967constexpr std::optional<usize>
968next_unset(Store const& store, usize index) {
969 // Start our search at index + 1.
970 index = index + 1;
971
972 // Perhaps we are off the end? (This also handles the case of an empty type).
973 if (index >= store.size()) return {};
974
975 // Where is that starting index located in the word store?
976 using word_type = typename Store::word_type;
977 auto bits_per_word = BITS<word_type>;
978 auto [word_index, bit] = index_and_offset<word_type>(index);
979
980 // Iterate forward looking for a word with a new unset bit and use the lowest of those ...
981 for (auto i = word_index; i < store.words(); ++i) {
982 auto w = store.word(i);
983 if (i == word_index) {
984 // First word -- turn all the bits before our starting bit into ones so we don't see them as unset.
985 set_bits(w, 0, bit);
986 }
987 if (i == store.words() - 1) {
988 // Final word may have some unused zero bits that we need to replace with ones.
989 auto last_occupied_bit = bit_offset<word_type>(store.size() - 1);
990 set_bits(w, last_occupied_bit + 1, bits_per_word);
991 }
992 if (auto loc = lowest_unset_bit(w)) return i * bits_per_word + loc.value();
993 }
994 return {};
995}
996
1011template<BitStore Store>
1012constexpr std::optional<usize>
1013previous_unset(Store const& store, usize index) {
1014 // Edge case: If the store is empty or we are at the start, there are no previous set bits.
1015 if (is_empty(store) || index == 0) return {};
1016
1017 // Silently fix large indices and also adjust the index down a slot.
1018 if (index > store.size()) index = store.size();
1019 index--;
1020
1021 // Where is that starting index located in the word store?
1022 using word_type = typename Store::word_type;
1023 auto bits_per_word = BITS<word_type>;
1024 auto [word_index, bit] = index_and_offset<word_type>(index);
1025
1026 // Iterate backwards looking for a word with a new unset bit and use the highest of those ...
1027 for (auto i = word_index + 1; i--;) {
1028 auto w = store.word(i);
1029 if (i == word_index) {
1030 // First word -- set all the bits after our starting bit into ones so we don't see them as unset.
1031 set_bits(w, bit + 1, bits_per_word);
1032 }
1033 if (i == store.words() - 1) {
1034 // Final word may have some unused zero bits that we need to replace with ones.
1035 auto last_occupied_bit = bit_offset<word_type>(store.size() - 1);
1036 set_bits(w, last_occupied_bit + 1, bits_per_word);
1037 }
1038 if (auto loc = highest_unset_bit(w)) return i * bits_per_word + loc.value();
1039 }
1040 return {};
1041}
1042
1046
1059template<BitStore Store>
1060constexpr auto
1061bits(Store const& store) {
1062 return BitsIter<const Store, true>(&store, 0);
1063}
1064
1078template<BitStore Store>
1079constexpr auto
1080bits(Store& store) {
1081 return BitsIter<Store, false>(&store, 0);
1082}
1083
1095template<BitStore Store>
1096constexpr auto
1097set_bits(Store const& store) {
1098 return SetBitsIter<Store>(&store);
1099}
1100
1112template<BitStore Store>
1113constexpr auto
1114unset_bits(Store const& store) {
1115 return UnsetBitsIter<Store>(&store);
1116}
1117
1135template<BitStore Store>
1136constexpr auto
1137store_words(Store const& store) {
1138 return WordsIter<Store>(&store);
1139}
1140
1151template<BitStore Store>
1152constexpr auto
1153to_words(Store const& store) {
1154 return std::ranges::to<std::vector>(store_words(store));
1155}
1156
1160
1174template<BitStore Store>
1175static constexpr auto
1176span(Store const& store, usize begin, usize end) {
1177 // Check that the range is correctly formed & does not extend beyond the end of the bit-store.
1178 gf2_assert(begin <= end, "Span range [{}, {}) is mis-ordered.", begin, end);
1179 gf2_assert(end <= store.size(), "Span end {} extends beyond the store end {}.", end, store.size());
1180
1181 // Get the zero-based word index and bit offset in that word for the begin location.
1182 using word_type = typename Store::word_type;
1183 auto bits_per_word = BITS<word_type>;
1184 auto [data_index, bit_offset] = index_and_offset<word_type>(begin);
1185
1186 // If the store is already a span, we need to adjust those values.
1187 bit_offset += store.offset();
1188 if (bit_offset >= bits_per_word) {
1189 data_index += 1;
1190 bit_offset %= bits_per_word;
1191 }
1192
1193 // Return the read-only bit-span over the underlying word data.
1194 return BitSpan<const word_type>(store.store() + data_index, bit_offset, end - begin);
1195}
1196
1211template<BitStore Store>
1212static constexpr auto
1213span(Store& store, usize begin, usize end) {
1214 // Check that the range is correctly formed & does not extend beyond the end of the bit-store.
1215 gf2_assert(begin <= end, "Span range [{}, {}) is mis-ordered.", begin, end);
1216 gf2_assert(end <= store.size(), "Span end {} extends beyond the store end {}.", end, store.size());
1217
1218 // Get the zero-based word index and bit offset in that word for the begin location.
1219 using word_type = typename Store::word_type;
1220 auto bits_per_word = BITS<word_type>;
1221 auto [data_index, bit_offset] = index_and_offset<word_type>(begin);
1222
1223 // If the store is already a span, we need to adjust those values.
1224 bit_offset += store.offset();
1225 if (bit_offset >= bits_per_word) {
1226 data_index += 1;
1227 bit_offset %= bits_per_word;
1228 }
1229
1230 // Return the read-only bit-span over the underlying word data.
1231 return BitSpan<word_type>(store.store() + data_index, bit_offset, end - begin);
1232}
1233
1237
1251template<BitStore Store>
1252constexpr auto
1253sub(Store const& store, usize begin, usize end) {
1254 return BitVec<typename Store::word_type>::from(gf2::span(store, begin, end));
1255}
1256
1260
1281template<BitStore Store>
1282constexpr void
1284 auto sz = store.size();
1285 gf2_assert(at <= sz, "Oops, split point {} is beyond the end of the bit-store {}", at, sz);
1286 left.clear();
1287 right.clear();
1288 left.append(span(store, 0, at));
1289 right.append(span(store, at, sz));
1290}
1291
1308template<BitStore Store>
1309constexpr auto
1310split(Store const& store, usize at) {
1312 split(store, at, left, right);
1313 return std::pair{left, right};
1314}
1315
1328template<BitStore Lhs, BitStore Rhs>
1329auto
1330join(Lhs const& lhs, Rhs const& rhs) {
1331 auto lhs_size = lhs.size();
1332 auto rhs_size = rhs.size();
1333
1334 using word_type = typename Lhs::word_type;
1335 BitVec<word_type> result(lhs_size + rhs_size);
1336
1337 result.span(0, lhs_size).copy(lhs);
1338 result.span(lhs_size, lhs_size + rhs_size).copy(rhs);
1339 return result;
1340}
1341
1345
1360template<BitStore Store>
1361constexpr void
1363 // Nothing to do if the store is empty or has just one element. With two bits `ab` we fill `dst` with `a0b`.
1364 auto sz = store.size();
1365 if (sz < 2) {
1366 dst.resize(sz);
1367 dst.copy(store);
1368 return;
1369 }
1370
1371 // Make sure `dst` is large enough to hold the riffled bits (a bit too big but we fix that below).
1372 dst.resize(2 * sz);
1373 auto dst_words = dst.words();
1374
1375 // Riffle each word in the store into two adjacent words in `dst`.
1376 for (auto i = 0uz; i < store.words(); ++i) {
1377 auto [lo, hi] = riffle(store.word(i));
1378 dst.set_word(2 * i, lo);
1379
1380 // Note that `hi` may be completely superfluous ...
1381 if (2 * i + 1 < dst_words) dst.set_word(2 * i + 1, hi);
1382 }
1383
1384 // If this bit-store was say `abcde` then `dst` will now be `a0b0c0d0e0`. Pop the last 0.
1385 dst.pop();
1386}
1387
1400template<BitStore Store>
1401constexpr auto
1402riffle(Store const& store) {
1404 riffle(store, result);
1405 return result;
1406}
1407
1411
1429template<BitStore Store>
1430static std::string
1431to_binary_string(Store const& store, std::string_view sep = "", std::string_view pre = "", std::string_view post = "") {
1432 // Edge case ...
1433 if (is_empty(store)) { return std::format("{}{}", pre, post); }
1434
1435 // Start with the raw binary string by iterating over the words ...
1436 // We preallocate enough space to hold the entire string (actually this is usually a bit bigger than we need).
1437 auto num_words = store.words();
1438 std::string binary_string;
1439 binary_string.reserve(num_words * BITS<typename Store::word_type>);
1440 for (auto i = 0uz; i < num_words; ++i) {
1441 auto w = reverse_bits(store.word(i));
1442 binary_string.append(gf2::to_binary_string(w));
1443 }
1444
1445 // The last word may not be fully occupied and we need to remove the spurious zeros
1446 binary_string.resize(store.size());
1447
1448 // If there is no separator, return early ...
1449 if (sep.empty()) { return std::format("{}{}{}", pre, binary_string, post); }
1450
1451 // Build `result` with separators between each character ...
1452 std::string result;
1453 result.reserve(pre.size() + store.size() * (sep.size() + 1) + post.size());
1454 result.append(pre);
1455 for (auto i = 0uz; i < store.size(); ++i) {
1456 result.push_back(binary_string[i]);
1457 if (i != store.size() - 1) { result.append(sep); }
1458 }
1459 result.append(post);
1460 return result;
1461}
1462
1480template<BitStore Store>
1481static std::string
1482to_string(Store const& store, std::string_view sep = "", std::string_view pre = "", std::string_view post = "") {
1483 return to_binary_string(store, sep, pre, post);
1484}
1485
1498template<BitStore Store>
1499static std::string
1500to_pretty_string(Store const& store) {
1501 return to_binary_string(store, ",", "[", "]");
1502}
1503
1534template<BitStore Store>
1535static std::string
1536to_hex_string(Store const& store) {
1537 // Edge case: No bits in the store, return the empty string.
1538 if (is_empty(store)) return "";
1539
1540 // The number of digits in the output string. Generally hexadecimal but the last may be to a lower base.
1541 auto digits = (store.size() + 3) / 4;
1542
1543 // Preallocate space allowing for a possible lower base on the last digit such as "_2".
1544 std::string result;
1545 result.reserve(digits + 2);
1546
1547 // Reverse the bits in each word to vector-order and get the fully padded hex string representation.
1548 using word_type = typename Store::word_type;
1549 for (auto i = 0uz; i < store.words(); ++i) {
1550 auto w = reverse_bits(store.word(i));
1551 result.append(gf2::to_hex_string<word_type>(w));
1552 }
1553
1554 // Last word may not be fully occupied and padded with spurious zeros so we truncate the output string.
1555 result.resize(digits);
1556
1557 // Every four elements is encoded by a single hex digit but `size()` may not be a multiple of 4.
1558 auto k = store.size() % 4;
1559 if (k != 0) {
1560 // That last hex digit should really be encoded to a lower base -- 2, 4 or 8.
1561 // We compute the number represented by the trailing `k` elements in the bit-vector.
1562 int num = 0;
1563 for (auto i = 0uz; i < k; ++i)
1564 if (get(store, store.size() - 1 - i)) num |= 1 << i;
1565
1566 // Convert that number to hex & use it to *replace* the last hex digit in our `result` string.
1567 // Also append the appropriate base to the output string so that the last digit can be interpreted properly.
1568 result.resize(result.size() - 1);
1569 result.append(std::format("{:X}.{}", num, 1 << k));
1570 }
1571 return result;
1572}
1573
1577template<BitStore Store>
1578static std::string
1579describe(Store const& store) {
1580 using word_type = typename Store::word_type;
1581 auto bits_per_word = BITS<word_type>;
1582 std::string result;
1583 result.reserve(1000);
1584 result += std::format("binary format: {}\n", to_binary_string(store));
1585 result += std::format("hex format: {}\n", to_hex_string(store));
1586 result += std::format("number of bits: {}\n", store.size());
1587 result += std::format("number of set bits: {}\n", count_ones(store));
1588 result += std::format("number of unset bits: {}\n", count_zeros(store));
1589 result += std::format("bits per word: {}\n", bits_per_word);
1590 result += std::format("word count: {}\n", store.words());
1591 result += "words in hex: [";
1592 for (auto i = 0uz; i < store.words(); ++i) {
1593 auto w = store.word(i);
1594 result += std::format("{:0{}X}", w, (bits_per_word + 3) / 4);
1595 result += std::format("{:x}", store.word(i));
1596 if (i + 1 < store.words()) result += ", ";
1597 }
1598 result += "]\n";
1599 return result;
1600}
1601
1603template<BitStore Store>
1604inline std::ostream&
1605operator<<(Store const& store, std::ostream& s) {
1606 return s << to_string(store);
1607}
1608
1612
1617// # Example
1625template<BitStore Lhs, BitStore Rhs>
1626constexpr bool
1627operator==(Lhs const& lhs, Rhs const& rhs) {
1628 if constexpr (!std::same_as<typename Lhs::word_type, typename Rhs::word_type>) return false;
1629 if (&lhs != &rhs) {
1630 if (lhs.size() != rhs.size()) return false;
1631 for (auto i = 0uz; i < lhs.words(); ++i)
1632 if (lhs.word(i) != rhs.word(i)) return false;
1633 }
1634 return true;
1635}
1636
1640
1654template<BitStore Store>
1655constexpr void
1656operator<<=(Store& store, usize shift) {
1657 // Edge cases where there is nothing to do.
1658 if (shift == 0 || store.size() == 0) return;
1659
1660 // Perhaps we are shifting all the bits out and are left with all zeros.
1661 if (shift >= store.size()) {
1662 set_all(store, false);
1663 return;
1664 }
1665
1666 // For larger shifts, we can efficiently shift by whole words first.
1667 using word_type = typename Store::word_type;
1668 auto bits_per_word = BITS<word_type>;
1669 usize word_shift = shift / bits_per_word;
1670 usize end_word = store.words() - word_shift;
1671
1672 // Do the whole word shifts first, pushing in zero words to fill the empty slots.
1673 if (word_shift > 0) {
1674 // Shift whole words -- starting at the beginning of the store.
1675 for (auto i = 0uz; i < end_word; ++i) store.set_word(i, store.word(i + word_shift));
1676
1677 // Fill in the high order words with zeros.
1678 for (auto i = end_word; i < store.words(); ++i) store.set_word(i, 0);
1679
1680 // How many bits are left to shift?
1681 shift -= word_shift * bits_per_word;
1682 }
1683
1684 // Perhaps there are some partial word shifts left to do.
1685 if (shift != 0) {
1686 // Do the "interior" words where the shift moves bits from one word to the next.
1687 if (end_word > 0) {
1688 auto shift_complement = bits_per_word - shift;
1689 for (auto i = 0uz; i < end_word - 1; ++i) {
1690 auto lo = static_cast<word_type>(store.word(i) >> shift);
1691 auto hi = static_cast<word_type>(store.word(i + 1) << shift_complement);
1692 store.set_word(i, lo | hi);
1693 }
1694 }
1695
1696 // Do the last word.
1697 auto value = store.word(end_word - 1);
1698 store.set_word(end_word - 1, static_cast<word_type>(value >> shift));
1699 }
1700}
1701
1715template<BitStore Store>
1716constexpr void
1717operator>>=(Store& store, usize shift) {
1718 // Edge cases where there is nothing to do.
1719 if (shift == 0 || store.size() == 0) return;
1720
1721 // Perhaps we are shifting all the bits out and are left with all zeros.
1722 if (shift >= store.size()) {
1723 set_all(store, false);
1724 return;
1725 }
1726
1727 // For larger shifts, we can efficiently shift by whole words first.
1728 using word_type = typename Store::word_type;
1729 auto bits_per_word = BITS<word_type>;
1730 usize word_shift = shift / bits_per_word;
1731
1732 // Do the whole word shifts first, pushing in zero words to fill the empty slots.
1733 if (word_shift > 0) {
1734 // Shift whole words -- starting at the end of the store.
1735 for (auto i = store.words(); i-- > word_shift;) store.set_word(i, store.word(i - word_shift));
1736
1737 // Fill in the low order words with zeros.
1738 for (auto i = 0uz; i < word_shift; ++i) store.set_word(i, 0);
1739
1740 // How many bits are left to shift?
1741 shift -= word_shift * bits_per_word;
1742 }
1743
1744 // Perhaps there are some partial word shifts left to do.
1745 if (shift != 0) {
1746 // Do the "interior" words where the shift moves bits from one word to the next.
1747 auto shift_complement = bits_per_word - shift;
1748 for (auto i = store.words(); i-- > word_shift + 1;) {
1749 auto lo = static_cast<word_type>(store.word(i - 1) >> shift_complement);
1750 auto hi = static_cast<word_type>(store.word(i) << shift);
1751 store.set_word(i, lo | hi);
1752 }
1753 }
1754
1755 // Do the "first" word.
1756 auto value = store.word(word_shift);
1757 store.set_word(word_shift, static_cast<word_type>(value << shift));
1758}
1759
1763
1777template<BitStore Store>
1778constexpr auto
1779operator<<(Store const& store, usize shift) {
1780 auto result = BitVec<typename Store::word_type>::from(store);
1781 result <<= shift;
1782 return result;
1783}
1784
1798template<BitStore Store>
1799constexpr auto
1800operator>>(Store const& store, usize shift) {
1801 auto result = BitVec<typename Store::word_type>::from(store);
1802 result >>= shift;
1803 return result;
1804}
1805
1809
1820template<BitStore Lhs, BitStore Rhs>
1821 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1822constexpr void
1823operator^=(Lhs& lhs, Rhs const& rhs) {
1824 gf2_assert(lhs.size() == rhs.size(), "Lengths do not match: {} != {}.", lhs.size(), rhs.size());
1825 for (auto i = 0uz; i < lhs.words(); ++i) lhs.set_word(i, lhs.word(i) ^ rhs.word(i));
1826}
1827
1838template<BitStore Lhs, BitStore Rhs>
1839 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1840constexpr void
1841operator&=(Lhs& lhs, Rhs const& rhs) {
1842 gf2_assert(lhs.size() == rhs.size(), "Lengths do not match: {} != {}.", lhs.size(), rhs.size());
1843 for (auto i = 0uz; i < lhs.words(); ++i) lhs.set_word(i, lhs.word(i) & rhs.word(i));
1844}
1845
1856template<BitStore Lhs, BitStore Rhs>
1857 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1858constexpr void
1859operator|=(Lhs& lhs, Rhs const& rhs) {
1860 gf2_assert(lhs.size() == rhs.size(), "Lengths do not match: {} != {}.", lhs.size(), rhs.size());
1861 for (auto i = 0uz; i < lhs.words(); ++i) lhs.set_word(i, lhs.word(i) | rhs.word(i));
1862}
1863
1867
1877template<BitStore Store>
1878constexpr auto
1879operator~(Store const& store) {
1880 auto result = BitVec<typename Store::word_type>::from(store);
1881 result.flip_all();
1882 return result;
1883}
1884
1896template<BitStore Lhs, BitStore Rhs>
1897 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1898constexpr auto
1899operator^(Lhs const& lhs, Rhs const& rhs) {
1900 auto result = BitVec<typename Lhs::word_type>::from(lhs);
1901 result ^= rhs;
1902 return result;
1903}
1904
1916template<BitStore Lhs, BitStore Rhs>
1917 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1918constexpr auto
1919operator&(Lhs const& lhs, Rhs const& rhs) {
1920 auto result = BitVec<typename Lhs::word_type>::from(lhs);
1921 result &= rhs;
1922 return result;
1923}
1924
1936template<BitStore Lhs, BitStore Rhs>
1937 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1938constexpr auto
1939operator|(Lhs const& lhs, Rhs const& rhs) {
1940 auto result = BitVec<typename Lhs::word_type>::from(lhs);
1941 result |= rhs;
1942 return result;
1943}
1944
1948
1961template<BitStore Lhs, BitStore Rhs>
1962 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1963constexpr void
1964operator+=(Lhs& lhs, Rhs const& rhs) {
1965 lhs ^= rhs;
1966}
1967
1980template<BitStore Lhs, BitStore Rhs>
1981 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1982constexpr void
1983operator-=(Lhs& lhs, Rhs const& rhs) {
1984 lhs ^= rhs;
1985}
1986
1990
2004template<BitStore Lhs, BitStore Rhs>
2005 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2006constexpr auto
2007operator+(Lhs const& lhs, Rhs const& rhs) {
2008 auto result = BitVec<typename Lhs::word_type>::from(lhs);
2009 result += rhs;
2010 return result;
2011}
2012
2026template<BitStore Lhs, BitStore Rhs>
2027 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2028constexpr auto
2029operator-(Lhs const& lhs, Rhs const& rhs) {
2030 auto result = BitVec<typename Lhs::word_type>::from(lhs);
2031 result -= rhs;
2032 return result;
2033}
2034
2038
2054template<BitStore Lhs, BitStore Rhs>
2055 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2056constexpr bool
2057dot(Lhs const& lhs, Rhs const& rhs) {
2058 gf2_debug_assert_eq(lhs.size(), rhs.size(), "Length mismatch {} != {}", lhs.size(), rhs.size());
2059 using word_type = typename Lhs::word_type;
2060 auto sum = word_type{0};
2061 for (auto i = 0uz; i < lhs.words(); ++i) { sum ^= static_cast<word_type>(lhs.word(i) & rhs.word(i)); }
2062 return count_ones(sum) % 2 == 1;
2063}
2064
2080template<BitStore Lhs, BitStore Rhs>
2081 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2082constexpr bool
2083operator*(Lhs const& lhs, Rhs const& rhs) {
2084 return dot(lhs, rhs);
2085}
2086
2090
2103template<BitStore Lhs, BitStore Rhs>
2104 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2105auto
2106convolve(Lhs const& lhs, Rhs const& rhs) {
2107 using word_type = typename Lhs::word_type;
2108 auto bits_per_word = BITS<word_type>;
2109
2110 // Edge case: if either store is empty then the convolution is empty.
2111 if (lhs.is_empty() || rhs.is_empty()) return BitVec<word_type>{};
2112
2113 // Set up the result bit-vector.
2114 auto result = BitVec<word_type>::zeros(lhs.size() + rhs.size() - 1);
2115
2116 // If either vector is all zeros then the convolution is all zeros.
2117 if (lhs.none() || rhs.none()) return result;
2118
2119 // Only need to consider words in `rhs` up to and including the one holding its final set bit.
2120 // We have already checked that `rhs` is not all zeros so we know there is a last set bit!
2121 auto rhs_words_end = word_index<word_type>(rhs.last_set().value()) + 1;
2122
2123 // Initialize `result` by copying the live words from `rhs`
2124 for (auto i = 0uz; i < rhs_words_end; ++i) result.set_word(i, rhs.word(i));
2125
2126 // Work backwards from the last set bit in `lhs` (which we know exists as we checked `lhs` is not all zeros).
2127 for (auto i = lhs.last_set().value(); i-- > 0;) {
2128 word_type prev = 0;
2129 for (auto j = 0uz; j < result.words(); ++j) {
2130 auto left = static_cast<word_type>(prev >> (bits_per_word - 1));
2131 prev = result.word(j);
2132 result.set_word(j, static_cast<word_type>(prev << 1) | left);
2133 }
2134
2135 if (lhs.get(i)) {
2136 for (auto j = 0uz; j < rhs_words_end; ++j) result.set_word(j, result.word(j) ^ rhs.word(j));
2137 }
2138 }
2139 return result;
2140}
2141
2143
2144} // namespace gf2
2145
2147template<gf2::BitStore Store>
2148struct std::formatter<Store> {
2149 // Parse a bit-store format specifier where where we recognize {:p} and {:x}
2150 constexpr auto parse(std::format_parse_context const& ctx) {
2151 auto it = ctx.begin();
2152 while (it != ctx.end() && *it != '}') {
2153 switch (*it) {
2154 case 'p': m_pretty = true; break;
2155 case 'x': m_hex = true; break;
2156 default: m_error = true;
2157 }
2158 ++it;
2159 }
2160 return it;
2161 }
2162
2163 // Format the bit-store according using the appropriate string conversion function.
2164 template<class FormatContext>
2165 auto format(Store const& rhs, FormatContext& ctx) const {
2166 // Was there a format specification error?
2167 if (m_error) return std::format_to(ctx.out(), "'UNRECOGNIZED FORMAT SPECIFIER FOR BIT-STORE'");
2168
2169 // Special handling requested?
2170 if (m_hex) return std::format_to(ctx.out(), "{}", to_hex_string(rhs));
2171 if (m_pretty) return std::format_to(ctx.out(), "{}", to_pretty_string(rhs));
2172
2173 // Default
2174 return std::format_to(ctx.out(), "{}", to_string(rhs));
2175 }
2176
2177 bool m_hex = false;
2178 bool m_pretty = false;
2179 bool m_error = false;
2180};
Utility functions that work on primitive unsigned word types. See the Unsigned page for more detail...
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:33
A bit_span is a non-owning view of contiguous bits in a bit-store. .
Definition BitSpan.h:30
A dynamically-sized vector over GF(2) with bit elements compactly stored in a standard vector of prim...
Definition BitVec.h:23
auto copy(Src src)
Copies the bits from an unsigned integral src value and returns a reference to this for chaining.
Definition BitVec.h:1127
constexpr auto span(usize begin, usize end) const
Returns an immutable bit-span encompassing the store's bits in the half-open range [begin,...
Definition BitVec.h:1489
constexpr usize size() const
Returns the number of bit-elements in the bit-vector.
Definition BitVec.h:50
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:642
constexpr usize words() const
Returns the number of words in the bit-vector's underlying word store.
Definition BitVec.h:63
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:194
constexpr BitVec & resize(usize n)
Resize the bit-vector so that its size() is n.
Definition BitVec.h:582
constexpr BitVec & clear()
Removes all elements from the bit-vector so size()==0.
Definition BitVec.h:563
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:98
static constexpr BitVec from(Src src)
Factory method to construct a bit-vector by copying all the bits from any Unsigned instance....
Definition BitVec.h:257
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:668
Two iterators over all the bits in a bit-store — one const and the other non-const....
Definition Iterators.h:38
An iterator over the index locations of the set bits in a bit-store. You get this iterator by calli...
Definition Iterators.h:123
An iterator over the index locations of the unset bits in a bit-store. You get this iterator by call...
Definition Iterators.h:205
An iterator over the "words" underlying a bit-store. You get this iterator by calling the BitStore::...
Definition Iterators.h:287
A concept that is satisfied by all bit-vector-like types, which we refer to as bit-stores.
Definition BitStore.h:70
The Unsigned concept is the same as std::unsigned_integral. It is satisfied by all primitive unsigned...
Definition Unsigned.h:24
The namespace for the gf2 library.
Definition assert.h:119
constexpr std::optional< usize > first_unset(Store const &store)
Returns the index of the first unset bit in the bit-store or {} if no bits are unset.
Definition BitStore.h:903
constexpr void operator<<=(Store &store, usize shift)
In-place left shift of a bit-store by shift bits.
Definition BitStore.h:1656
auto join(Lhs const &lhs, Rhs const &rhs)
Returns a new bit-vector formed by joining two bit-stores lhs and rhs.
Definition BitStore.h:1330
constexpr bool operator==(Lhs const &lhs, Rhs const &rhs)
Checks that any pair of bit-stores are equal in content.
Definition BitStore.h:1627
constexpr auto operator^(Lhs const &lhs, Rhs const &rhs)
Returns the XOR of two equal-sized bit-stores as a new bit-vector.
Definition BitStore.h:1899
constexpr std::optional< usize > first_set(Store const &store)
Returns the index of the first set bit in the bit-store or {} if no bits are set.
Definition BitStore.h:770
constexpr bool is_empty(Store const &store)
Returns true if the store is empty, false otherwise.
Definition BitStore.h:337
constexpr std::optional< usize > next_unset(Store const &store, usize index)
Returns the index of the next unset bit after index in the store or {} if no more unset bits exist.
Definition BitStore.h:968
constexpr auto operator>>(Store const &store, usize shift)
Returns a new bit-vector that is lhs shifted right by shift bits.
Definition BitStore.h:1800
constexpr auto operator&(Lhs const &lhs, Rhs const &rhs)
Returns the AND of two equal-sized bit-stores as a new bit-vector.
Definition BitStore.h:1919
constexpr void operator|=(Lhs &lhs, Rhs const &rhs)
In-place OR of one bit-store with an equal-sized bit-store.
Definition BitStore.h:1859
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.h:573
constexpr u8 bit_offset(usize i)
Returns the bit position within the containing word for bit element i.
Definition Unsigned.h:556
static std::string to_string(Store const &store, std::string_view sep="", std::string_view pre="", std::string_view post="")
Returns a binary string representation of a store.
Definition BitStore.h:1482
std::ostream & operator<<(Store const &store, std::ostream &s)
The usual output stream operator for a bit-store.
Definition BitStore.h:1605
constexpr auto operator*(BitMat< Word > const &lhs, Rhs const &rhs)
Operator form for bit-matrix, bit-store multiplication, M * v, returning a new bit-vector.
Definition BitMat.h:2535
constexpr void operator^=(Lhs &lhs, Rhs const &rhs)
In-place XOR of one bit-store with an equal-sized bit-store.
Definition BitStore.h:1823
auto convolve(Lhs const &lhs, Rhs const &rhs)
Convolutions:
Definition BitStore.h:2106
constexpr Word reverse_bits(Word word)
Returns a copy of word with all its bits reversed.
Definition Unsigned.h:243
constexpr auto & flip_all(Store &store)
Flips the value of the bits in the store and returns the store for chaining.
Definition BitStore.h:445
constexpr auto ref(Store &store, usize i)
Returns a "reference" to the bit element i in the given bit-store.
Definition BitStore.h:189
constexpr usize count_zeros(Store const &store)
Returns the number of unset bits in the store.
Definition BitStore.h:691
constexpr auto & flip(Store &store, usize i)
Flips the value of the bit-element at the given index and returns the store for chaining.
Definition BitStore.h:269
constexpr auto operator~(Store const &store)
Returns a new bit-vector that has the same bits as a bit-store but with all the bits flipped.
Definition BitStore.h:1879
constexpr bool get(Store const &store, usize i)
Returns the bool value of the bit at index i in the given bit-store.
Definition BitStore.h:163
constexpr auto set_bits(Store const &store)
Returns an iterator over the indices of any set bits in the bit-store.
Definition BitStore.h:1097
constexpr bool back(Store const &store)
Returns true if the final bit element is set, false otherwise.
Definition BitStore.h:225
constexpr auto dot(BitMat< Word > const &lhs, Rhs const &rhs)
Bit-matrix, bit-store multiplication, M * v, returning a new bit-vector.
Definition BitMat.h:2523
constexpr auto & random_fill(Store &store, double p=0.5, u64 seed=0)
Fill the store with random bits and returns the store for chaining.
Definition BitStore.h:631
static std::string to_pretty_string(Store const &store)
Returns a "pretty" string representation of a store.
Definition BitStore.h:1500
std::uint64_t u64
Word type alias for a 64-bit unsigned integer.
Definition Unsigned.h:39
constexpr std::optional< usize > previous_set(Store const &store, usize index)
Returns the index of the previous set bit before index in the store or {} if there are none.
Definition BitStore.h:857
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.h:172
constexpr void operator+=(Lhs &lhs, Rhs const &rhs)
In-place addition of one bit-store with an equal-sized bit-store.
Definition BitStore.h:1964
constexpr auto store_words(Store const &store)
Returns a const iterator over all the words underlying the bit-store.
Definition BitStore.h:1137
constexpr std::optional< usize > next_set(Store const &store, usize index)
Returns the index of the next set bit after index in the store or {} if no more set bits exist.
Definition BitStore.h:819
constexpr auto & set_all(Store &store, bool value=true)
Sets the bits in the store to the boolean value and returns the store for chaining.
Definition BitStore.h:428
constexpr bool all(Store const &store)
Returns true if all bits in the store are set, false otherwise.
Definition BitStore.h:375
constexpr auto sub(Store const &store, usize begin, usize end)
Returns a clone of the elements in the half-open range [begin, end) as a new bit-vector.
Definition BitStore.h:1253
static constexpr auto span(Store const &store, usize begin, usize end)
Constructs a read-only bit-span over the const bit-store store for its bits in the range [begin,...
Definition BitStore.h:1176
constexpr auto to_words(Store const &store)
Returns a copy of the words underlying this bit-store.
Definition BitStore.h:1153
constexpr auto operator-(Lhs const &lhs, Rhs const &rhs)
Subtracts two equal-sized bit-stores and returns the result as a new bit-vector.
Definition BitStore.h:2029
constexpr void split(Store const &store, usize at, BitVec< typename Store::word_type > &left, BitVec< typename Store::word_type > &right)
Views a bit-store as two parts containing the elements [0, at) and [at, size()) respectively....
Definition BitStore.h:1283
constexpr auto bits(Store const &store)
Returns a const iterator over the bool values of the bits in the const bit-store.
Definition BitStore.h:1061
constexpr auto unset_bits(Store const &store)
Returns an iterator over the indices of any unset bits in the bit-store.
Definition BitStore.h:1114
constexpr void operator-=(Lhs &lhs, Rhs const &rhs)
In-place difference of one bit-store with an equal-sized bit-store.
Definition BitStore.h:1983
constexpr auto & set(Store &store, usize i, bool value=true)
Sets the bit-element at the given index to the specified boolean value and returns the store for chai...
Definition BitStore.h:244
constexpr void operator>>=(Store &store, usize shift)
In-place right shift of a store by shift bits.
Definition BitStore.h:1717
static std::string to_hex_string(Store const &store)
Returns the "hex" string representation of the bits in the bit-store.
Definition BitStore.h:1536
constexpr usize count_ones(Store const &store)
Returns the number of set bits in the store.
Definition BitStore.h:674
constexpr usize leading_zeros(Store const &store)
Returns the number of leading zeros in the store.
Definition BitStore.h:708
constexpr std::optional< usize > previous_unset(Store const &store, usize index)
Returns the index of the previous unset bit before index in the store or {} if no more unset bits exi...
Definition BitStore.h:1013
constexpr usize word_index(usize i)
Returns the index of the Unsigned word holding bit element i.
Definition Unsigned.h:540
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.h:590
constexpr auto & copy(Src src, Store &store)
Copies the bits from an unsigned integral src value and returns the store for chaining.
Definition BitStore.h:474
constexpr bool front(Store const &store)
Returns true if the first bit element is set, false otherwise.
Definition BitStore.h:207
constexpr usize trailing_zeros(Store const &store)
Returns the number of trailing zeros in the store.
Definition BitStore.h:729
std::size_t usize
Word type alias for the platform's "native"-sized unsigned integer.
Definition Unsigned.h:42
constexpr std::optional< u8 > lowest_unset_bit(Word word)
Returns the index of the lowest unset bit in an Unsigned or std::nullopt if there are no unset bits.
Definition Unsigned.h:454
constexpr bool none(Store const &store)
Returns true if no bits in the store are set, false otherwise.
Definition BitStore.h:408
constexpr std::optional< u8 > highest_set_bit(Word word)
Returns the index of the highest set bit in an Unsigned or std::nullopt if no bits are set.
Definition Unsigned.h:438
constexpr Word MAX
The Unsigned instance with all its bits set to 1.
Definition Unsigned.h:82
constexpr auto operator+(Lhs const &lhs, Rhs const &rhs)
Adds two equal-sized bit-stores and returns the result as a new bit-vector.
Definition BitStore.h:2007
constexpr u8 BITS
The number of bits in an Unsigned returned as a u8.
Definition Unsigned.h:55
constexpr bool any(Store const &store)
Returns true if at least one bit in the store is set, false otherwise.
Definition BitStore.h:354
constexpr std::optional< usize > last_unset(Store const &store)
Returns the index of the last unset bit in the bit-store or {} if no bits are unset.
Definition BitStore.h:936
static std::string describe(Store const &store)
Detailed Description of a Bit-Store.
Definition BitStore.h:1579
constexpr void riffle(Store const &store, BitVec< typename Store::word_type > &dst)
Interleaves the bits of a bit-store with zeros storing the result into the passed bit-vector dst.
Definition BitStore.h:1362
constexpr void operator&=(Lhs &lhs, Rhs const &rhs)
In-place AND of one bit-store with an equal-sized bit-store.
Definition BitStore.h:1841
constexpr std::optional< u8 > lowest_set_bit(Word word)
Returns the index of the lowest set bit in an Unsigned or std::nullopt if no bits are set.
Definition Unsigned.h:418
static std::string to_binary_string(Store const &store, std::string_view sep="", std::string_view pre="", std::string_view post="")
Returns a binary string representation of a store.
Definition BitStore.h:1431
constexpr auto & swap(Store &store, usize i0, usize i1)
Swaps the bits in the bit-store at indices i0 and i1 and returns the store for chaining.
Definition BitStore.h:297
constexpr std::optional< usize > last_set(Store const &store)
Returns the index of the last set bit in the bit-store or {} if no bits are set.
Definition BitStore.h:795
constexpr auto operator|(Lhs const &lhs, Rhs const &rhs)
Returns the OR of two equal-sized bit-stores as a new bit-vector.
Definition BitStore.h:1939
constexpr std::optional< u8 > highest_unset_bit(Word word)
Returns the index of the highest unset bit in an Unsigned or std::nullopt if there are none.
Definition Unsigned.h:470