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 <iterator>
19#include <optional>
20#include <ranges>
21#include <string>
22#include <string_view>
23#include <utility>
24#include <vector>
25#include <type_traits>
26
27// --------------------------------------------------------------------------------------------------------------------
28// The BitStore concept ...
29// --------------------------------------------------------------------------------------------------------------------
30
31namespace gf2 {
32
70template<typename Store>
71concept BitStore = requires(Store store, const Store const_store) {
73 typename Store::word_type;
75
77 { const_store.size() } -> std::same_as<usize>;
78
80 { const_store.store() } -> std::same_as<const typename Store::word_type*>;
81
84 { store.store() } -> std::convertible_to<const typename Store::word_type*>;
85
88 { const_store.offset() } -> std::same_as<u8>;
89
95 { const_store.words() } -> std::same_as<usize>;
96
108 { const_store.word(usize{}) } -> std::same_as<typename Store::word_type>;
109
122 { store.set_word(usize{}, typename Store::word_type{}) } -> std::same_as<void>;
123};
124
125} // namespace gf2
126
127// --------------------------------------------------------------------------------------------------------------------
128// Forward Declarations of BitStore Related Types.
129// --------------------------------------------------------------------------------------------------------------------
130
131namespace gf2 {
132
133// clang-format off
134template<usize N, Unsigned Word> class BitArray;
135template<Unsigned Word> class BitVector;
136template<Unsigned Word> class BitSpan;
137template<BitStore Store> class BitRef;
138template<BitStore Store, bool is_const> class Bits;
139template<BitStore Store> class SetBits;
140template<BitStore Store> class UnsetBits;
141template<BitStore Store> class Words;
142// clang-format on
143
144} // namespace gf2
145
146// --------------------------------------------------------------------------------------------------------------------
147// Free Functions for BitStore Types.
148// --------------------------------------------------------------------------------------------------------------------
149
150namespace gf2 {
153
166template<BitStore Store>
167constexpr bool
168get(Store const& store, usize i) {
169 gf2_debug_assert(i < store.size(), "Index {} is out of bounds for store of length {}", i, store.size());
171 return store.word(word_index) & word_mask;
172}
173
181// # Panics
195template<BitStore Store>
196constexpr auto
197ref(Store& store, usize i) {
198 gf2_debug_assert(i < store.size(), "Index {} is out of bounds for store of length {}", i, store.size());
199 return BitRef{&store, i};
200}
201
214template<BitStore Store>
215constexpr bool
216front(Store const& store) {
217 gf2_debug_assert(!is_empty(store), "The store is empty!");
218 return get(store, 0);
219}
220
233template<BitStore Store>
234constexpr bool
235back(Store const& store) {
236 gf2_debug_assert(!is_empty(store), "The store is empty!");
237 return get(store, store.size() - 1);
238}
239
252template<BitStore Store>
253constexpr void
254set(Store& store, usize i, bool value = true) {
255 gf2_debug_assert(i < store.size(), "Index {} is out of bounds for store of length {}", i, store.size());
257 auto word_value = store.word(word_index);
258 auto bit_value = (word_value & word_mask) != 0;
259 if (bit_value != value) store.set_word(word_index, word_value ^ word_mask);
260}
261
277template<BitStore Store>
278constexpr void
279flip(Store& store, usize i) {
280 gf2_debug_assert(i < store.size(), "Index {} is out of bounds for store of length {}", i, store.size());
282 auto word_value = store.word(word_index);
283 store.set_word(word_index, word_value ^ word_mask);
284}
285
305template<BitStore Store>
306constexpr void
307swap(Store& store, usize i0, usize i1) {
308 gf2_debug_assert(i0 < store.size(), "index {} is out of bounds for a store of length {}", i0, store.size());
309 gf2_debug_assert(i1 < store.size(), "index {} is out of bounds for a store of length {}", i1, store.size());
310 if (i0 != i1) {
311 auto [w0, mask0] = index_and_mask<typename Store::word_type>(i0);
312 auto [w1, mask1] = index_and_mask<typename Store::word_type>(i1);
313 auto word0 = store.word(w0);
314 auto word1 = store.word(w1);
315 auto val0 = (word0 & mask0) != 0;
316 auto val1 = (word1 & mask1) != 0;
317 if (val0 != val1) {
318 if (w0 == w1) {
319 // Both bits are in the same word
320 store.set_word(w0, word0 ^ mask0 ^ mask1);
321 } else {
322 // The two bits are in different words
323 store.set_word(w0, word0 ^ mask0);
324 store.set_word(w1, word1 ^ mask1);
325 }
326 }
327 }
328}
329
333
343template<BitStore Store>
344constexpr bool
345is_empty(Store const& store) {
346 return store.size() == 0;
347}
348
361template<BitStore Store>
362constexpr bool
363any(Store const& store) {
364 for (auto i = 0uz; i < store.words(); ++i)
365 if (store.word(i) != 0) return true;
366 return false;
367}
368
383template<BitStore Store>
384constexpr bool
385all(Store const& store) {
386 auto num_words = store.words();
387 if (num_words == 0) { return true; }
388
389 // Check the fully occupied words ...
390 using word_type = typename Store::word_type;
391 for (auto i = 0uz; i < num_words - 1; ++i) {
392 if (store.word(i) != MAX<word_type>) return false;
393 }
394
395 // Last word might not be fully occupied ...
396 auto bits_per_word = BITS<word_type>;
397 auto tail_bits = store.size() % bits_per_word;
398 auto unused_bits = tail_bits == 0 ? 0 : bits_per_word - tail_bits;
399 auto last_word_max = MAX<word_type> >> unused_bits;
400 if (store.word(num_words - 1) != last_word_max) return false;
401
402 return true;
403}
404
417template<BitStore Store>
418constexpr bool
419none(Store const& store) {
420 return !any(store);
421}
422
426
437template<BitStore Store>
438constexpr void
439set_all(Store& store, bool value = true) {
440 using word_type = typename Store::word_type;
441 auto word_value = value ? MAX<word_type> : word_type{0};
442 for (auto i = 0uz; i < store.words(); ++i) store.set_word(i, word_value);
443}
444
453template<BitStore Store>
454constexpr void
455flip_all(Store& store) {
456 using word_type = typename Store::word_type;
457 for (auto i = 0uz; i < store.words(); ++i) store.set_word(i, static_cast<word_type>(~store.word(i)));
458}
459
463
483template<Unsigned Src, BitStore Store>
484constexpr void
485copy(Src src, Store& store) {
486 constexpr auto src_bits = BITS<Src>;
487 gf2_assert(store.size() == src_bits, "Lengths do not match: {} != {}.", store.size(), src_bits);
488
489 using dst_type = typename Store::word_type;
490 constexpr auto dst_bpw = BITS<dst_type>;
491
492 if constexpr (src_bits <= dst_bpw) {
493 // The source fits into a single store word. We can pad the source to get it to the correct size.
494 // It happens that casting zero-pads --- but `set_word` only ever looks at the low `src_bits` piece anyway.
495 store.set_word(0, static_cast<dst_type>(src));
496 } else {
497 // The source spans some integer number of store words (we assume it is an integer number).
498 // Proceed by nibbling off destination-sized words from the larger source until its exhausted.
499 constexpr auto dst_words = src_bits / dst_bpw;
500 constexpr auto dst_mask = MAX<dst_type>;
501 for (auto i = 0uz; i < dst_words; ++i, src >>= dst_bpw) {
502 auto src_word = static_cast<dst_type>(src & dst_mask);
503 store.set_word(i, src_word);
504 }
505 }
506}
507
526template<typename Iter, BitStore Store>
527 requires std::is_unsigned_v<typename std::iterator_traits<Iter>::value_type>
528constexpr void
529copy(Iter src_begin, Iter src_end, Store& store) {
530
531 // What are the two word types (without any const qualifiers)?
532 using dst_type = std::remove_const_t<typename Store::word_type>;
533 using src_type = typename std::iterator_traits<Iter>::value_type;
534
535 // Numbers of bits per respective word.
536 constexpr auto dst_bpw = BITS<dst_type>;
537 constexpr auto src_bpw = BITS<src_type>;
538
539 // Size of the source in words
540 auto src_words = static_cast<std::size_t>(std::distance(src_begin, src_end));
541
542 // The sizes in bits must match ...
543 gf2_assert(store.size() == src_words * src_bpw, "Lengths do not match: {} != {}.", store.size(),
544 src_words * src_bpw);
545
546 // What we do next depends on the size of the respective words.
547 if constexpr (src_bpw == dst_bpw) {
548 // Same sized words -- we can copy whole words directly ...
549 auto src_iter = src_begin;
550 for (auto i = 0uz; i < store.words(); ++i, ++src_iter) store.set_word(i, static_cast<dst_type>(*src_iter));
551 } else {
552 // Otherwise proceed by copying one source word at a time into the appropriate span in the destination store.
553 // The actual copying is done by the earlier method that copies any unsigned into the store,
554 auto src_iter = src_begin;
555 auto dst_begin = 0uz;
556 for (auto i = 0uz; i < src_words; ++i, ++src_iter, dst_begin += src_bpw) {
557 auto dst_span = span(store, dst_begin, dst_begin + src_bpw);
558 copy(*src_iter, dst_span);
559 }
560 }
561}
562
580template<BitStore Src, BitStore Dst>
581constexpr void
582copy(Src const& src, Dst& dst) {
583 // The sizes must match ...
584 gf2_assert(dst.size() == src.size(), "Lengths do not match: {} != {}.", dst.size(), src.size());
585
586 // What are the word types (without any const qualifiers)?
587 using dst_type = std::remove_const_t<typename Dst::word_type>;
588 using src_type = std::remove_const_t<typename Src::word_type>;
589
590 // Numbers of bits per respective words
591 constexpr auto dst_bpw = BITS<dst_type>;
592 constexpr auto src_bpw = BITS<src_type>;
593
594 // What we do next depends on the size of the respective words.
595 if constexpr (dst_bpw == src_bpw) {
596 // Same sized words holding the same number of bits => same number of words in each ...
597 for (auto i = 0uz; i < dst.words(); ++i) {
598 auto value = static_cast<dst_type>(src.word(i));
599 dst.set_word(i, value);
600 }
601 } else if constexpr (dst_bpw > src_bpw) {
602 // The destination word type is larger -- some number of source words fit into each destination word.
603 // We assume that the destination word size is an integer multiple of the source word size.
604 constexpr auto ratio = dst_bpw / src_bpw;
605
606 auto src_words = src.words();
607 for (auto i = 0uz; i < dst.words(); ++i) {
608 // Combine `ratio` source words into a single destination word being careful not to run off the end of `src`
609 auto value = ZERO<dst_type>;
610 for (auto j = 0uz, src_index = i * ratio; j < ratio && src_index < src_words; ++j, ++src_index) {
611 auto src_word = static_cast<dst_type>(src.word(src_index));
612 value |= (src_word << (j * src_bpw));
613 }
614 dst.set_word(i, value);
615 }
616 } else {
617 // The destination word type is smaller -- each source word becomes multiple destination words.
618 // We assume that the source word size is an integer multiple of the destination word size.
619 constexpr auto ratio = src_bpw / dst_bpw;
620
621 auto dst_words = dst.words();
622 auto dst_mask = MAX<dst_type>;
623 for (auto i = 0uz; i < src.words(); ++i) {
624 // Split each source word into `ratio` destination words being careful not to run off the end of `dst`
625 auto src_word = src.word(i);
626 for (auto j = 0uz, dst_index = i * ratio; j < ratio && dst_index < dst_words; ++j, ++dst_index) {
627 dst.set_word(dst_index, static_cast<dst_type>(src_word & dst_mask));
628 src_word >>= dst_bpw;
629 }
630 }
631 }
632}
633
649template<usize N, BitStore Store>
650constexpr void
651copy(std::bitset<N> const& src, Store& store) {
652 // The sizes must match
653 gf2_assert(store.size() == N, "Lengths do not match: {} != {}.", store.size(), N);
654
655 // Doing this the dumb way as there is no standard access to the bitset's underlying store
656 set_all(store, false);
657 for (auto i = 0uz; i < N; ++i)
658 if (src[i]) set(store, i);
659}
660
670template<BitStore Store>
671constexpr void
672copy(Store& store, std::invocable<usize> auto f) {
673 set_all(store, false);
674 for (auto i = 0uz; i < store.size(); ++i)
675 if (f(i) == true) set(store, i);
676}
677
695template<BitStore Store>
696constexpr void
697fill_random(Store& store, double p = 0.5, u64 seed = 0) {
698 // Keep a single static RNG per thread for all calls to this method, seeded with entropy on the first call.
699 thread_local RNG rng;
700
701 // Edge case handling ...
702 if (p < 0) {
703 set_all(store, false);
704 return;
705 }
706
707 // Scale p by 2^64 to remove floating point arithmetic from the main loop below.
708 // If we determine p rounds to 1 then we can just set all elements to 1 and return early.
709 p = p * 0x1p64 + 0.5;
710 if (p >= 0x1p64) {
711 set_all(store);
712 return;
713 }
714
715 // p does not round to 1 so we use a 64-bit URNG and check each draw against the 64-bit scaled p.
716 auto scaled_p = static_cast<u64>(p);
717
718 // If a seed was provided, set the RNG's seed to it. Otherwise, we carry on from where we left off.
719 u64 old_seed = rng.seed();
720 if (seed != 0) rng.set_seed(seed);
721
722 set_all(store, false);
723 for (auto i = 0uz; i < store.size(); ++i)
724 if (rng.u64() < scaled_p) set(store, i);
725
726 // Restore the old seed if necessary.
727 if (seed != 0) rng.set_seed(old_seed);
728}
729
733
743template<BitStore Store>
744constexpr usize
745count_ones(Store const& store) {
746 usize count = 0;
747 for (auto i = 0uz; i < store.words(); ++i) count += static_cast<usize>(gf2::count_ones(store.word(i)));
748 return count;
749}
750
760template<BitStore Store>
761constexpr usize
762count_zeros(Store const& store) {
763 return store.size() - count_ones(store);
764}
765
777template<BitStore Store>
778constexpr usize
779leading_zeros(Store const& store) {
780 // Note: Even if the last word is not fully occupied, we know unused bits are set to 0.
781 auto bits_per_word = BITS<typename Store::word_type>;
782 for (auto i = 0uz; i < store.words(); ++i) {
783 auto w = store.word(i);
784 if (w != 0) return i * bits_per_word + static_cast<usize>(gf2::trailing_zeros(w));
785 }
786 return store.size();
787}
788
798template<BitStore Store>
799constexpr usize
800trailing_zeros(Store const& store) {
801 if (is_empty(store)) return 0;
802
803 // The last word may not be fully occupied so we need to subtract the number of unused bits.
804 auto bits_per_word = BITS<typename Store::word_type>;
805 auto tail_bits = store.size() % bits_per_word;
806 auto unused_bits = tail_bits == 0 ? 0 : bits_per_word - tail_bits;
807 auto num_words = store.words();
808 auto i = num_words;
809 while (i--) {
810 auto w = store.word(i);
811 if (w != 0) {
812 auto whole_count = (num_words - i - 1) * bits_per_word;
813 auto partial_count = static_cast<usize>(gf2::leading_zeros(w)) - unused_bits;
814 return whole_count + partial_count;
815 }
816 }
817 return store.size();
818}
819
823
839template<BitStore Store>
840constexpr std::optional<usize>
841first_set(Store const& store) {
842 // Iterate forward looking for a word with a set bit and use the lowest of those ...
843 // Remember that any unused bits in the final word are guaranteed to be unset.
844 auto bits_per_word = BITS<typename Store::word_type>;
845 for (auto i = 0uz; i < store.words(); ++i) {
846 if (auto loc = lowest_set_bit(store.word(i))) return i * bits_per_word + loc.value();
847 }
848 return {};
849}
850
864template<BitStore Store>
865constexpr std::optional<usize>
866last_set(Store const& store) {
867 // Iterate backward looking for a word with a set bit and use the highest of those ...
868 // Remember that any unused bits in the final word are guaranteed to be unset.
869 auto bits_per_word = BITS<typename Store::word_type>;
870 for (auto i = store.words(); i--;) {
871 if (auto loc = highest_set_bit(store.word(i))) return i * bits_per_word + loc.value();
872 }
873 return {};
874}
875
888template<BitStore Store>
889constexpr std::optional<usize>
890next_set(Store const& store, usize index) {
891 // Start our search at index + 1.
892 index = index + 1;
893
894 // Perhaps we are off the end? (This also handles the case of an empty type).
895 if (index >= store.size()) return {};
896
897 // Where is that starting index located in the word store?
898 using word_type = typename Store::word_type;
899 auto bits_per_word = BITS<word_type>;
900 auto [word_index, bit] = index_and_offset<word_type>(index);
901
902 // Iterate forward looking for a word with a new set bit and use the lowest one ...
903 for (auto i = word_index; i < store.words(); ++i) {
904 auto w = store.word(i);
905 if (i == word_index) {
906 // First word -- turn all the bits before our starting bit into zeros so we don't see them as set.
907 reset_bits(w, 0, bit);
908 }
909 if (auto loc = lowest_set_bit(w)) return i * bits_per_word + loc.value();
910 }
911 return {};
912}
913
926template<BitStore Store>
927constexpr std::optional<usize>
928previous_set(Store const& store, usize index) {
929 // Edge case: If the store is empty or we are at the start, there are no previous set bits.
930 if (is_empty(store) || index == 0) return {};
931
932 // Silently fix large indices and also adjust the index down a slot.
933 if (index > store.size()) index = store.size();
934 index--;
935
936 // Where is that starting index located in the word store?
937 using word_type = typename Store::word_type;
938 auto bits_per_word = BITS<word_type>;
939 auto [word_index, bit] = index_and_offset<word_type>(index);
940
941 // Iterate backwards looking for a word with a new set bit and use the highest one ...
942 for (auto i = word_index + 1; i--;) {
943 auto w = store.word(i);
944 if (i == word_index) {
945 // First word -- turn all higher bits after our starting bit into zeros so we don't see them as set.
946 reset_bits(w, bit + 1, bits_per_word);
947 }
948 if (auto loc = highest_set_bit(w)) return i * bits_per_word + loc.value();
949 }
950 return {};
951}
952
956
972template<BitStore Store>
973constexpr std::optional<usize>
974first_unset(Store const& store) {
975 // Iterate forward looking for a word with a unset bit and use the lowest of those ...
976 using word_type = typename Store::word_type;
977 auto bits_per_word = BITS<word_type>;
978 for (auto i = 0uz; i < store.words(); ++i) {
979 auto w = store.word(i);
980 if (i == store.words() - 1) {
981 // Final word may have some unused zero bits that we need to replace with ones.
982 auto last_occupied_bit = bit_offset<word_type>(store.size() - 1);
983 set_bits(w, last_occupied_bit + 1, bits_per_word);
984 }
985 if (auto loc = lowest_unset_bit(w)) return i * bits_per_word + loc.value();
986 }
987 return {};
988}
989
1005template<BitStore Store>
1006constexpr std::optional<usize>
1007last_unset(Store const& store) {
1008 // Iterate backwards looking for a word with a unset bit and use the highest of those ...
1009 using word_type = typename Store::word_type;
1010 auto bits_per_word = BITS<word_type>;
1011 for (auto i = store.words(); i--;) {
1012 auto w = store.word(i);
1013 if (i == store.words() - 1) {
1014 // Final word may have some unused zero bits that we need to replace with ones.
1015 auto last_occupied_bit = bit_offset<word_type>(store.size() - 1);
1016 set_bits(w, last_occupied_bit + 1, bits_per_word);
1017 }
1018 if (auto loc = highest_unset_bit(w)) return i * bits_per_word + loc.value();
1019 }
1020 return {};
1021}
1022
1037template<BitStore Store>
1038constexpr std::optional<usize>
1039next_unset(Store const& store, usize index) {
1040 // Start our search at index + 1.
1041 index = index + 1;
1042
1043 // Perhaps we are off the end? (This also handles the case of an empty type).
1044 if (index >= store.size()) return {};
1045
1046 // Where is that starting index located in the word store?
1047 using word_type = typename Store::word_type;
1048 auto bits_per_word = BITS<word_type>;
1049 auto [word_index, bit] = index_and_offset<word_type>(index);
1050
1051 // Iterate forward looking for a word with a new unset bit and use the lowest of those ...
1052 for (auto i = word_index; i < store.words(); ++i) {
1053 auto w = store.word(i);
1054 if (i == word_index) {
1055 // First word -- turn all the bits before our starting bit into ones so we don't see them as unset.
1056 set_bits(w, 0, bit);
1057 }
1058 if (i == store.words() - 1) {
1059 // Final word may have some unused zero bits that we need to replace with ones.
1060 auto last_occupied_bit = bit_offset<word_type>(store.size() - 1);
1061 set_bits(w, last_occupied_bit + 1, bits_per_word);
1062 }
1063 if (auto loc = lowest_unset_bit(w)) return i * bits_per_word + loc.value();
1064 }
1065 return {};
1066}
1067
1082template<BitStore Store>
1083constexpr std::optional<usize>
1084previous_unset(Store const& store, usize index) {
1085 // Edge case: If the store is empty or we are at the start, there are no previous set bits.
1086 if (is_empty(store) || index == 0) return {};
1087
1088 // Silently fix large indices and also adjust the index down a slot.
1089 if (index > store.size()) index = store.size();
1090 index--;
1091
1092 // Where is that starting index located in the word store?
1093 using word_type = typename Store::word_type;
1094 auto bits_per_word = BITS<word_type>;
1095 auto [word_index, bit] = index_and_offset<word_type>(index);
1096
1097 // Iterate backwards looking for a word with a new unset bit and use the highest of those ...
1098 for (auto i = word_index + 1; i--;) {
1099 auto w = store.word(i);
1100 if (i == word_index) {
1101 // First word -- set all the bits after our starting bit into ones so we don't see them as unset.
1102 set_bits(w, bit + 1, bits_per_word);
1103 }
1104 if (i == store.words() - 1) {
1105 // Final word may have some unused zero bits that we need to replace with ones.
1106 auto last_occupied_bit = bit_offset<word_type>(store.size() - 1);
1107 set_bits(w, last_occupied_bit + 1, bits_per_word);
1108 }
1109 if (auto loc = highest_unset_bit(w)) return i * bits_per_word + loc.value();
1110 }
1111 return {};
1112}
1113
1117
1131template<BitStore Store>
1132constexpr auto
1133bits(Store const& store) {
1134 return Bits<const Store, true>(&store, 0);
1135}
1136
1151template<BitStore Store>
1152constexpr auto
1153bits(Store& store) {
1154 return Bits<Store, false>(&store, 0);
1155}
1156
1168template<BitStore Store>
1169constexpr auto
1170set_bits(Store const& store) {
1171 return SetBits<Store>(&store);
1172}
1173
1185template<BitStore Store>
1186constexpr auto
1187unset_bits(Store const& store) {
1188 return UnsetBits<Store>(&store);
1189}
1190
1209template<BitStore Store>
1210constexpr auto
1211store_words(Store const& store) {
1212 return Words<Store>(&store);
1213}
1214
1233template<BitStore Store>
1234constexpr void
1235to_words(Store const& store, std::output_iterator<typename Store::word_type> auto out) {
1236 for (auto i = 0uz; i < store.words(); ++i, ++out) *out = store.word(i);
1237}
1238
1251template<BitStore Store>
1252constexpr auto
1253to_words(Store const& store) {
1254 return std::ranges::to<std::vector>(store_words(store));
1255}
1256
1260
1275template<BitStore Store>
1276static constexpr auto
1277span(Store const& store, usize begin, usize end) {
1278 // Check that the range is correctly formed & does not extend beyond the end of the bit-store.
1279 gf2_assert(begin <= end, "Span range [{}, {}) is mis-ordered.", begin, end);
1280 gf2_assert(end <= store.size(), "Span end {} extends beyond the store end {}.", end, store.size());
1281
1282 // Get the zero-based word index and bit offset in that word for the begin location.
1283 using word_type = typename Store::word_type;
1284 auto bits_per_word = BITS<word_type>;
1285 auto [data_index, bit_offset] = index_and_offset<word_type>(begin);
1286
1287 // If the store is already a span, we need to adjust those values.
1288 bit_offset += store.offset();
1289 if (bit_offset >= bits_per_word) {
1290 data_index += 1;
1291 bit_offset %= bits_per_word;
1292 }
1293
1294 // Return the read-only bit-span over the underlying word data.
1295 return BitSpan<const word_type>(store.store() + data_index, bit_offset, end - begin);
1296}
1297
1313template<BitStore Store>
1314static constexpr auto
1315span(Store& store, usize begin, usize end) {
1316 // Check that the range is correctly formed & does not extend beyond the end of the bit-store.
1317 gf2_assert(begin <= end, "Span range [{}, {}) is mis-ordered.", begin, end);
1318 gf2_assert(end <= store.size(), "Span end {} extends beyond the store end {}.", end, store.size());
1319
1320 // Get the zero-based word index and bit offset in that word for the begin location.
1321 using word_type = typename Store::word_type;
1322 auto bits_per_word = BITS<word_type>;
1323 auto [data_index, bit_offset] = index_and_offset<word_type>(begin);
1324
1325 // If the store is already a span, we need to adjust those values.
1326 bit_offset += store.offset();
1327 if (bit_offset >= bits_per_word) {
1328 data_index += 1;
1329 bit_offset %= bits_per_word;
1330 }
1331
1332 // Return the read-only bit-span over the underlying word data.
1333 return BitSpan<word_type>(store.store() + data_index, bit_offset, end - begin);
1334}
1335
1339
1354template<BitStore Store>
1355constexpr auto
1356sub(Store const& store, usize begin, usize end) {
1357 return BitVector<typename Store::word_type>::from(gf2::span(store, begin, end));
1358}
1359
1363
1385template<BitStore Store>
1386constexpr void
1389 auto sz = store.size();
1390 gf2_assert(at <= sz, "Oops, split point {} is beyond the end of the bit-store {}", at, sz);
1391 left.resize(at);
1392 right.resize(sz - at);
1393 copy(span(store, 0, at), left);
1394 copy(span(store, at, sz), right);
1395}
1396
1414template<BitStore Store>
1415constexpr auto
1416split(Store const& store, usize at) {
1418 split(store, at, left, right);
1419 return std::pair{left, right};
1420}
1421
1434template<BitStore Lhs, BitStore Rhs>
1435auto
1436join(Lhs const& lhs, Rhs const& rhs) {
1437 auto lhs_size = lhs.size();
1438 auto rhs_size = rhs.size();
1439
1440 using word_type = typename Lhs::word_type;
1441 BitVector<word_type> result(lhs_size + rhs_size);
1442
1443 result.span(0, lhs_size).copy(lhs);
1444 result.span(lhs_size, lhs_size + rhs_size).copy(rhs);
1445 return result;
1446}
1447
1451
1466template<BitStore Store>
1467constexpr void
1469 // Nothing to do if the store is empty or has just one element. With two bits `ab` we fill `dst` with `a0b`.
1470 auto sz = store.size();
1471 if (sz < 2) {
1472 dst.resize(sz);
1473 dst.copy(store);
1474 return;
1475 }
1476
1477 // Make sure `dst` is large enough to hold the riffled bits (a bit too big but we fix that below).
1478 dst.resize(2 * sz);
1479 auto dst_words = dst.words();
1480
1481 // Riffle each word in the store into two adjacent words in `dst`.
1482 for (auto i = 0uz; i < store.words(); ++i) {
1483 auto [lo, hi] = riffle(store.word(i));
1484 dst.set_word(2 * i, lo);
1485
1486 // Note that `hi` may be completely superfluous ...
1487 if (2 * i + 1 < dst_words) dst.set_word(2 * i + 1, hi);
1488 }
1489
1490 // If this bit-store was say `abcde` then `dst` will now be `a0b0c0d0e0`. Pop the last 0.
1491 dst.pop();
1492}
1493
1506template<BitStore Store>
1507constexpr auto
1508riffle(Store const& store) {
1510 riffle(store, result);
1511 return result;
1512}
1513
1517
1535template<BitStore Store>
1536static std::string
1537to_binary_string(Store const& store, std::string_view sep = "", std::string_view pre = "", std::string_view post = "") {
1538 // Edge case ...
1539 if (is_empty(store)) { return std::format("{}{}", pre, post); }
1540
1541 // Start with the raw binary string by iterating over the words ...
1542 // We preallocate enough space to hold the entire string (actually this is usually a bit bigger than we need).
1543 auto num_words = store.words();
1544 std::string binary_string;
1545 binary_string.reserve(num_words * BITS<typename Store::word_type>);
1546 for (auto i = 0uz; i < num_words; ++i) {
1547 auto w = reverse_bits(store.word(i));
1548 binary_string.append(gf2::to_binary_string(w));
1549 }
1550
1551 // The last word may not be fully occupied and we need to remove the spurious zeros
1552 binary_string.resize(store.size());
1553
1554 // If there is no separator, return early ...
1555 if (sep.empty()) { return std::format("{}{}{}", pre, binary_string, post); }
1556
1557 // Build `result` with separators between each character ...
1558 std::string result;
1559 result.reserve(pre.size() + store.size() * (sep.size() + 1) + post.size());
1560 result.append(pre);
1561 for (auto i = 0uz; i < store.size(); ++i) {
1562 result.push_back(binary_string[i]);
1563 if (i != store.size() - 1) { result.append(sep); }
1564 }
1565 result.append(post);
1566 return result;
1567}
1568
1586template<BitStore Store>
1587static std::string
1588to_string(Store const& store, std::string_view sep = "", std::string_view pre = "", std::string_view post = "") {
1589 return to_binary_string(store, sep, pre, post);
1590}
1591
1604template<BitStore Store>
1605static std::string
1606to_pretty_string(Store const& store) {
1607 return to_binary_string(store, ",", "[", "]");
1608}
1609
1640template<BitStore Store>
1641static std::string
1642to_hex_string(Store const& store) {
1643 // Edge case: No bits in the store, return the empty string.
1644 if (is_empty(store)) return "";
1645
1646 // The number of digits in the output string. Generally hexadecimal but the last may be to a lower base.
1647 auto digits = (store.size() + 3) / 4;
1648
1649 // Preallocate space allowing for a possible lower base on the last digit such as "_2".
1650 std::string result;
1651 result.reserve(digits + 2);
1652
1653 // Reverse the bits in each word to vector-order and get the fully padded hex string representation.
1654 using word_type = typename Store::word_type;
1655 for (auto i = 0uz; i < store.words(); ++i) {
1656 auto w = reverse_bits(store.word(i));
1657 result.append(gf2::to_hex_string<word_type>(w));
1658 }
1659
1660 // Last word may not be fully occupied and padded with spurious zeros so we truncate the output string.
1661 result.resize(digits);
1662
1663 // Every four elements is encoded by a single hex digit but `size()` may not be a multiple of 4.
1664 auto k = store.size() % 4;
1665 if (k != 0) {
1666 // That last hex digit should really be encoded to a lower base -- 2, 4 or 8.
1667 // We compute the number represented by the trailing `k` elements in the bit-vector.
1668 int num = 0;
1669 for (auto i = 0uz; i < k; ++i)
1670 if (get(store, store.size() - 1 - i)) num |= 1 << i;
1671
1672 // Convert that number to hex & use it to *replace* the last hex digit in our `result` string.
1673 // Also append the appropriate base to the output string so that the last digit can be interpreted properly.
1674 result.resize(result.size() - 1);
1675 result.append(std::format("{:X}.{}", num, 1 << k));
1676 }
1677 return result;
1678}
1679
1683template<BitStore Store>
1684static std::string
1685describe(Store const& store) {
1686 using word_type = typename Store::word_type;
1687 auto bits_per_word = BITS<word_type>;
1688 std::string result;
1689 result.reserve(1000);
1690 result += std::format("binary format: {}\n", to_binary_string(store));
1691 result += std::format("hex format: {}\n", to_hex_string(store));
1692 result += std::format("number of bits: {}\n", store.size());
1693 result += std::format("number of set bits: {}\n", count_ones(store));
1694 result += std::format("number of unset bits: {}\n", count_zeros(store));
1695 result += std::format("bits per word: {}\n", bits_per_word);
1696 result += std::format("word count: {}\n", store.words());
1697 result += "words in hex: [";
1698 for (auto i = 0uz; i < store.words(); ++i) {
1699 auto w = store.word(i);
1700 result += std::format("{:0{}X}", w, (bits_per_word + 3) / 4);
1701 result += std::format("{:x}", store.word(i));
1702 if (i + 1 < store.words()) result += ", ";
1703 }
1704 result += "]\n";
1705 return result;
1706}
1707
1709template<BitStore Store>
1710inline std::ostream&
1711operator<<(Store const& store, std::ostream& s) {
1712 return s << to_string(store);
1713}
1714
1718
1723// # Example
1731template<BitStore Lhs, BitStore Rhs>
1732constexpr bool
1733operator==(Lhs const& lhs, Rhs const& rhs) {
1734 if constexpr (!std::same_as<typename Lhs::word_type, typename Rhs::word_type>) return false;
1735 if (&lhs != &rhs) {
1736 if (lhs.size() != rhs.size()) return false;
1737 for (auto i = 0uz; i < lhs.words(); ++i)
1738 if (lhs.word(i) != rhs.word(i)) return false;
1739 }
1740 return true;
1741}
1742
1746
1760template<BitStore Store>
1761constexpr void
1762operator<<=(Store& store, usize shift) {
1763 // Edge cases where there is nothing to do.
1764 if (shift == 0 || store.size() == 0) return;
1765
1766 // Perhaps we are shifting all the bits out and are left with all zeros.
1767 if (shift >= store.size()) {
1768 set_all(store, false);
1769 return;
1770 }
1771
1772 // For larger shifts, we can efficiently shift by whole words first.
1773 using word_type = typename Store::word_type;
1774 auto bits_per_word = BITS<word_type>;
1775 usize word_shift = shift / bits_per_word;
1776 usize end_word = store.words() - word_shift;
1777
1778 // Do the whole word shifts first, pushing in zero words to fill the empty slots.
1779 if (word_shift > 0) {
1780 // Shift whole words -- starting at the beginning of the store.
1781 for (auto i = 0uz; i < end_word; ++i) store.set_word(i, store.word(i + word_shift));
1782
1783 // Fill in the high order words with zeros.
1784 for (auto i = end_word; i < store.words(); ++i) store.set_word(i, 0);
1785
1786 // How many bits are left to shift?
1787 shift -= word_shift * bits_per_word;
1788 }
1789
1790 // Perhaps there are some partial word shifts left to do.
1791 if (shift != 0) {
1792 // Do the "interior" words where the shift moves bits from one word to the next.
1793 if (end_word > 0) {
1794 auto shift_complement = bits_per_word - shift;
1795 for (auto i = 0uz; i < end_word - 1; ++i) {
1796 auto lo = static_cast<word_type>(store.word(i) >> shift);
1797 auto hi = static_cast<word_type>(store.word(i + 1) << shift_complement);
1798 store.set_word(i, lo | hi);
1799 }
1800 }
1801
1802 // Do the last word.
1803 auto value = store.word(end_word - 1);
1804 store.set_word(end_word - 1, static_cast<word_type>(value >> shift));
1805 }
1806}
1807
1821template<BitStore Store>
1822constexpr void
1823operator>>=(Store& store, usize shift) {
1824 // Edge cases where there is nothing to do.
1825 if (shift == 0 || store.size() == 0) return;
1826
1827 // Perhaps we are shifting all the bits out and are left with all zeros.
1828 if (shift >= store.size()) {
1829 set_all(store, false);
1830 return;
1831 }
1832
1833 // For larger shifts, we can efficiently shift by whole words first.
1834 using word_type = typename Store::word_type;
1835 auto bits_per_word = BITS<word_type>;
1836 usize word_shift = shift / bits_per_word;
1837
1838 // Do the whole word shifts first, pushing in zero words to fill the empty slots.
1839 if (word_shift > 0) {
1840 // Shift whole words -- starting at the end of the store.
1841 for (auto i = store.words(); i-- > word_shift;) store.set_word(i, store.word(i - word_shift));
1842
1843 // Fill in the low order words with zeros.
1844 for (auto i = 0uz; i < word_shift; ++i) store.set_word(i, 0);
1845
1846 // How many bits are left to shift?
1847 shift -= word_shift * bits_per_word;
1848 }
1849
1850 // Perhaps there are some partial word shifts left to do.
1851 if (shift != 0) {
1852 // Do the "interior" words where the shift moves bits from one word to the next.
1853 auto shift_complement = bits_per_word - shift;
1854 for (auto i = store.words(); i-- > word_shift + 1;) {
1855 auto lo = static_cast<word_type>(store.word(i - 1) >> shift_complement);
1856 auto hi = static_cast<word_type>(store.word(i) << shift);
1857 store.set_word(i, lo | hi);
1858 }
1859 }
1860
1861 // Do the "first" word.
1862 auto value = store.word(word_shift);
1863 store.set_word(word_shift, static_cast<word_type>(value << shift));
1864}
1865
1869
1883template<BitStore Store>
1884constexpr auto
1885operator<<(Store const& store, usize shift) {
1886 auto result = BitVector<typename Store::word_type>::from(store);
1887 result <<= shift;
1888 return result;
1889}
1890
1904template<BitStore Store>
1905constexpr auto
1906operator>>(Store const& store, usize shift) {
1907 auto result = BitVector<typename Store::word_type>::from(store);
1908 result >>= shift;
1909 return result;
1910}
1911
1915
1926template<BitStore Lhs, BitStore Rhs>
1927 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1928constexpr void
1929operator^=(Lhs& lhs, Rhs const& rhs) {
1930 gf2_assert(lhs.size() == rhs.size(), "Lengths do not match: {} != {}.", lhs.size(), rhs.size());
1931 for (auto i = 0uz; i < lhs.words(); ++i) lhs.set_word(i, lhs.word(i) ^ rhs.word(i));
1932}
1933
1945template<BitStore Lhs, BitStore Rhs>
1946 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1947constexpr void
1948operator&=(Lhs& lhs, Rhs const& rhs) {
1949 gf2_assert(lhs.size() == rhs.size(), "Lengths do not match: {} != {}.", lhs.size(), rhs.size());
1950 for (auto i = 0uz; i < lhs.words(); ++i) lhs.set_word(i, lhs.word(i) & rhs.word(i));
1951}
1952
1964template<BitStore Lhs, BitStore Rhs>
1965 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
1966constexpr void
1967operator|=(Lhs& lhs, Rhs const& rhs) {
1968 gf2_assert(lhs.size() == rhs.size(), "Lengths do not match: {} != {}.", lhs.size(), rhs.size());
1969 for (auto i = 0uz; i < lhs.words(); ++i) lhs.set_word(i, lhs.word(i) | rhs.word(i));
1970}
1971
1975
1985template<BitStore Store>
1986constexpr auto
1987operator~(Store const& store) {
1988 auto result = BitVector<typename Store::word_type>::from(store);
1989 result.flip_all();
1990 return result;
1991}
1992
2005template<BitStore Lhs, BitStore Rhs>
2006 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2007constexpr auto
2008operator^(Lhs const& lhs, Rhs const& rhs) {
2010 result ^= rhs;
2011 return result;
2012}
2013
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) {
2031 result &= rhs;
2032 return result;
2033}
2034
2047template<BitStore Lhs, BitStore Rhs>
2048 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2049constexpr auto
2050operator|(Lhs const& lhs, Rhs const& rhs) {
2052 result |= rhs;
2053 return result;
2054}
2055
2059
2073template<BitStore Lhs, BitStore Rhs>
2074 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2075constexpr void
2076operator+=(Lhs& lhs, Rhs const& rhs) {
2077 lhs ^= rhs;
2078}
2079
2093template<BitStore Lhs, BitStore Rhs>
2094 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2095constexpr void
2096operator-=(Lhs& lhs, Rhs const& rhs) {
2097 lhs ^= rhs;
2098}
2099
2103
2118template<BitStore Lhs, BitStore Rhs>
2119 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2120constexpr auto
2121operator+(Lhs const& lhs, Rhs const& rhs) {
2123 result += rhs;
2124 return result;
2125}
2126
2141template<BitStore Lhs, BitStore Rhs>
2142 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2143constexpr auto
2144operator-(Lhs const& lhs, Rhs const& rhs) {
2146 result -= rhs;
2147 return result;
2148}
2149
2153
2170template<BitStore Lhs, BitStore Rhs>
2171 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2172constexpr bool
2173dot(Lhs const& lhs, Rhs const& rhs) {
2174 gf2_debug_assert_eq(lhs.size(), rhs.size(), "Length mismatch {} != {}", lhs.size(), rhs.size());
2175 using word_type = typename Lhs::word_type;
2176 auto sum = word_type{0};
2177 for (auto i = 0uz; i < lhs.words(); ++i) { sum ^= static_cast<word_type>(lhs.word(i) & rhs.word(i)); }
2178 return count_ones(sum) % 2 == 1;
2179}
2180
2197template<BitStore Lhs, BitStore Rhs>
2198 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2199constexpr bool
2200operator*(Lhs const& lhs, Rhs const& rhs) {
2201 return dot(lhs, rhs);
2202}
2203
2207
2220template<BitStore Lhs, BitStore Rhs>
2221 requires std::same_as<typename Lhs::word_type, typename Rhs::word_type>
2222auto
2223convolve(Lhs const& lhs, Rhs const& rhs) {
2224 using word_type = typename Lhs::word_type;
2225 auto bits_per_word = BITS<word_type>;
2226
2227 // Edge case: if either store is empty then the convolution is empty.
2228 if (lhs.is_empty() || rhs.is_empty()) return BitVector<word_type>{};
2229
2230 // Set up the result bit-vector.
2231 auto result = BitVector<word_type>::zeros(lhs.size() + rhs.size() - 1);
2232
2233 // If either vector is all zeros then the convolution is all zeros.
2234 if (lhs.none() || rhs.none()) return result;
2235
2236 // Only need to consider words in `rhs` up to and including the one holding its final set bit.
2237 // We have already checked that `rhs` is not all zeros so we know there is a last set bit!
2238 auto rhs_words_end = word_index<word_type>(rhs.last_set().value()) + 1;
2239
2240 // Initialize `result` by copying the live words from `rhs`
2241 for (auto i = 0uz; i < rhs_words_end; ++i) result.set_word(i, rhs.word(i));
2242
2243 // Work backwards from the last set bit in `lhs` (which we know exists as we checked `lhs` is not all zeros).
2244 for (auto i = lhs.last_set().value(); i-- > 0;) {
2245 word_type prev = 0;
2246 for (auto j = 0uz; j < result.words(); ++j) {
2247 auto left = static_cast<word_type>(prev >> (bits_per_word - 1));
2248 prev = result.word(j);
2249 result.set_word(j, static_cast<word_type>(prev << 1) | left);
2250 }
2251
2252 if (lhs.get(i)) {
2253 for (auto j = 0uz; j < rhs_words_end; ++j) result.set_word(j, result.word(j) ^ rhs.word(j));
2254 }
2255 }
2256 return result;
2257}
2258
2260
2261} // namespace gf2
2262
2264template<gf2::BitStore Store>
2265struct std::formatter<Store> {
2266 // Parse a bit-store format specifier where where we recognize {:p} and {:x}
2267 constexpr auto parse(std::format_parse_context const& ctx) {
2268 auto it = ctx.begin();
2269 while (it != ctx.end() && *it != '}') {
2270 switch (*it) {
2271 case 'p': m_pretty = true; break;
2272 case 'x': m_hex = true; break;
2273 default: m_error = true;
2274 }
2275 ++it;
2276 }
2277 return it;
2278 }
2279
2280 // Format the bit-store according using the appropriate string conversion function.
2281 template<class FormatContext>
2282 auto format(Store const& rhs, FormatContext& ctx) const {
2283 // Was there a format specification error?
2284 if (m_error) return std::format_to(ctx.out(), "'UNRECOGNIZED FORMAT SPECIFIER FOR BIT-STORE'");
2285
2286 // Special handling requested?
2287 if (m_hex) return std::format_to(ctx.out(), "{}", to_hex_string(rhs));
2288 if (m_pretty) return std::format_to(ctx.out(), "{}", to_pretty_string(rhs));
2289
2290 // Default
2291 return std::format_to(ctx.out(), "{}", to_string(rhs));
2292 }
2293
2294 bool m_hex = false;
2295 bool m_pretty = false;
2296 bool m_error = false;
2297};
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 BitVector.h:23
constexpr auto span(usize begin, usize end) const
Returns an immutable bit-span encompassing the bit-vector's bits in the half-open range [begin,...
Definition BitVector.h:1655
static constexpr BitVector from(Src src)
Factory method to construct a bit-vector by copying all the bits from any Unsigned instance....
Definition BitVector.h:259
static constexpr BitVector zeros(usize n)
Factory method to generate a bit-vector of length n where the elements are all 0.
Definition BitVector.h:196
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 BitVector.h:679
constexpr auto copy(Src src)
Copies all the bits from any unsigned integral src value to this equal-sized bit-vector....
Definition BitVector.h:1226
constexpr usize size() const
Returns the number of bit-elements in the bit-vector.
Definition BitVector.h:50
constexpr usize words() const
Returns the number of words in the bit-vector's underlying word store.
Definition BitVector.h:63
constexpr BitVector & resize(usize n)
Resize the bit-vector so that its size() is n.
Definition BitVector.h:619
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 BitVector.h:100
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:121
An iterator over the index locations of the unset bits in a bit-store. You get this iterator by call...
Definition Iterators.h:203
An iterator over the "words" underlying a bit-store. You get this iterator by calling the BitStore::...
Definition Iterators.h:285
A concept that is satisfied by all bit-vector-like types, which we refer to as bit-stores.
Definition BitStore.h:71
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 void set(Store &store, usize i, bool value=true)
Sets the bit-element at the given index to the specified boolean value (default value is true).
Definition BitStore.h:254
constexpr void set_all(Store &store, bool value=true)
Sets the bits in the store to the boolean value.
Definition BitStore.h:439
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:974
constexpr void operator<<=(Store &store, usize shift)
In-place left shift of a bit-store by shift bits.
Definition BitStore.h:1762
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:1436
constexpr bool operator==(Lhs const &lhs, Rhs const &rhs)
Checks that any pair of bit-stores are equal in content.
Definition BitStore.h:1733
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:2008
constexpr Word ZERO
The zero value for an Unsigned type.
Definition Unsigned.h:64
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:841
constexpr bool is_empty(Store const &store)
Returns true if the store is empty, false otherwise.
Definition BitStore.h:345
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:1039
constexpr void swap(Store &store, usize i0, usize i1)
Swaps the bits in the bit-store at indices i0 and i1.
Definition BitStore.h:307
constexpr auto operator>>(Store const &store, usize shift)
Returns a new bit-vector that is lhs shifted right by shift bits.
Definition BitStore.h:1906
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:2029
constexpr void operator|=(Lhs &lhs, Rhs const &rhs)
In-place OR of one bit-store with an equal-sized bit-store.
Definition BitStore.h:1967
constexpr auto operator*(BitMatrix< Word > const &lhs, Rhs const &rhs)
Operator form for bit-matrix, bit-store multiplication, M * v, returning a new bit-vector.
Definition BitMatrix.h:2587
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:580
constexpr u8 bit_offset(usize i)
Returns the bit position within the containing word for bit element i.
Definition Unsigned.h:563
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:1588
std::ostream & operator<<(Store const &store, std::ostream &s)
The usual output stream operator for a bit-store.
Definition BitStore.h:1711
constexpr void operator^=(Lhs &lhs, Rhs const &rhs)
In-place XOR of one bit-store with an equal-sized bit-store.
Definition BitStore.h:1929
auto convolve(Lhs const &lhs, Rhs const &rhs)
Convolutions:
Definition BitStore.h:2223
constexpr Word reverse_bits(Word word)
Returns a copy of word with all its bits reversed.
Definition Unsigned.h:249
constexpr auto ref(Store &store, usize i)
Returns a "reference" to the bit element i in the given bit-store.
Definition BitStore.h:197
constexpr usize count_zeros(Store const &store)
Returns the number of unset bits in the store.
Definition BitStore.h:762
constexpr void split(Store const &store, usize at, BitVector< typename Store::word_type > &left, BitVector< typename Store::word_type > &right)
Views a bit-store as two parts containing the elements [0, at) and [at, size()) respectively....
Definition BitStore.h:1387
constexpr auto dot(BitMatrix< Word > const &lhs, Rhs const &rhs)
Bit-matrix, bit-store multiplication, M * v, returning a new bit-vector.
Definition BitMatrix.h:2573
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:1987
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:168
constexpr auto set_bits(Store const &store)
Returns an iterator over the indices of any set bits in the bit-store.
Definition BitStore.h:1170
constexpr bool back(Store const &store)
Returns true if the final bit element is set, false otherwise.
Definition BitStore.h:235
static std::string to_pretty_string(Store const &store)
Returns a "pretty" string representation of a store.
Definition BitStore.h:1606
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:928
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:176
constexpr void operator+=(Lhs &lhs, Rhs const &rhs)
In-place addition of one bit-store with an equal-sized bit-store.
Definition BitStore.h:2076
constexpr auto store_words(Store const &store)
Returns a const iterator over all the words underlying the bit-store.
Definition BitStore.h:1211
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:890
constexpr bool all(Store const &store)
Returns true if all bits in the store are set, false otherwise.
Definition BitStore.h:385
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:1356
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:1277
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:2144
constexpr void flip_all(Store &store)
Flips the value of the bits in the store.
Definition BitStore.h:455
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:1133
constexpr auto unset_bits(Store const &store)
Returns an iterator over the indices of any unset bits in the bit-store.
Definition BitStore.h:1187
constexpr void operator-=(Lhs &lhs, Rhs const &rhs)
In-place difference of one bit-store with an equal-sized bit-store.
Definition BitStore.h:2096
constexpr void operator>>=(Store &store, usize shift)
In-place right shift of a store by shift bits.
Definition BitStore.h:1823
constexpr void flip(Store &store, usize i)
Flips the value of the bit-element at the given index.
Definition BitStore.h:279
static std::string to_hex_string(Store const &store)
Returns the "hex" string representation of the bits in the bit-store.
Definition BitStore.h:1642
constexpr usize count_ones(Store const &store)
Returns the number of set bits in the store.
Definition BitStore.h:745
constexpr usize leading_zeros(Store const &store)
Returns the number of leading zeros in the store.
Definition BitStore.h:779
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:1084
constexpr usize word_index(usize i)
Returns the index of the Unsigned word holding bit element i.
Definition Unsigned.h:547
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:597
constexpr void copy(Src src, Store &store)
Copies all the bits from any unsigned integral src value to an equal-sized bit-store.
Definition BitStore.h:485
constexpr bool front(Store const &store)
Returns true if the first bit element is set, false otherwise.
Definition BitStore.h:216
constexpr void to_words(Store const &store, std::output_iterator< typename Store::word_type > auto out)
Returns a copy of the words underlying this bit-store and puts them into the passed output iterator.
Definition BitStore.h:1235
constexpr usize trailing_zeros(Store const &store)
Returns the number of trailing zeros in the store.
Definition BitStore.h:800
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:461
constexpr bool none(Store const &store)
Returns true if no bits in the store are set, false otherwise.
Definition BitStore.h:419
constexpr void riffle(Store const &store, BitVector< 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:1468
constexpr void fill_random(Store &store, double p=0.5, u64 seed=0)
Fill the store with random bits based on an optional probability p and an optional seed for the RNG.
Definition BitStore.h:697
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:445
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:2121
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:363
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:1007
static std::string describe(Store const &store)
Detailed Description of a Bit-Store.
Definition BitStore.h:1685
constexpr void operator&=(Lhs &lhs, Rhs const &rhs)
In-place AND of one bit-store with an equal-sized bit-store.
Definition BitStore.h:1948
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:425
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:1537
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:866
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:2050
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:477