MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
bitset.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#pragma once
20
21#ifndef MTCORE_BITSET_HPP
22#define MTCORE_BITSET_HPP
23
24#include <limits>
25
26#include "optional.hpp"
27#include "result.hpp"
28#include "slice.hpp"
29
30namespace mtcore {
39
46 struct Bitset {
48 size_t len = 0;
49
56 [[nodiscard]] Result<void, AllocationError> init(mtcore::Allocator &allocator, const size_t total) {
57 ensure(!bits.head, "ALREADY ALLOCATED");
58 this->len = total;
59 const auto sliceLen = total / 64 + (total % 64 ? 1 : 0);
60 ensure(sliceLen > 0, "CANNOT CREATE EMPTY BITSET");
61
62 const auto allocBitsRes = allocator.create_many<u64>(sliceLen);
63 if (allocBitsRes.is_error()) {
64 return allocBitsRes.error();
65 }
66 bits = *allocBitsRes;
67 memset(bits.head, 0, sliceLen * sizeof(u64));
68 return success();
69 }
70
77 ensure(!bits.head, "ALREADY ALLOCATED");
78 return init(allocator, binaryStr.len, binaryStr);
79 }
80
89 const Slice<const char> binaryStr) {
90 ensure(!bits.head, "ALREADY ALLOCATED");
91 {
92 char cur = '\0';
93 auto iter = binaryStr.iter();
94 while (iter.next().copy_if_present(cur)) {
95 if (cur != '1' && cur != '0') {
97 }
98 }
99 }
100
101 auto initRes = init(allocator, total);
102 if (initRes.is_error()) {
104 }
105
106 char cur = '\0';
107 auto iter = binaryStr.iter();
108 size_t index = 0;
109 while (iter.next().copy_if_present(cur)) {
110 mtdefer { ++index; };
111 if (cur == '1') {
112 set(index, true);
113 }
114 }
115 return success();
116 }
117
122 void deinit(mtcore::Allocator &allocator) {
123 if (bits.head != nullptr) {
124 allocator.destroy_many(bits);
125 }
126 len = 0;
127 }
128
133 size_t pos;
135
136 [[nodiscard]] operator bool() const { return bs.at(pos); }
137 [[nodiscard]] bool val() const { return bs.at(pos); }
138 BitsetAccess &set(const bool newVal) {
139 bs.set(pos, newVal);
140 return *this;
141 }
142
143 BitsetAccess &operator=(const bool val) { return set(val); }
144 BitsetAccess &operator&=(const bool other) { return set(val() & other); }
145 BitsetAccess &operator|=(const bool other) { return set(val() | other); }
146 BitsetAccess &operator^=(const bool other) { return set(val() ^ other); }
147 };
148
154 [[nodiscard]] BitsetAccess operator[](const size_t index) {
155 ensure(bits.head, "BITSET NOT INITIALIZED");
156 ensure(index < len, "OUT OF BOUNDS ACCESS");
157 return {.pos = index, .bs = *this};
158 }
159
165 [[nodiscard]] bool operator[](const size_t index) const { return at(index); }
166
172 [[nodiscard]] size_t size() const { return len; }
173
179 [[nodiscard]] bool at(const size_t pos) const {
180 ensure(bits.head, "BITSET NOT INITIALIZED");
181 ensure(pos < len, "OUT OF BOUNDS ACCESS");
182 const auto block = pos / 64;
183 const auto bit = pos % 64;
184 const u64 bitMask = static_cast<u64>(0x1) << bit;
185 return (bits[block] & bitMask) != 0;
186 }
187
194 ensure(bits.head, "BITSET NOT INITIALIZED");
195 return clone(allocator, len);
196 }
197
204 Result<Bitset, AllocationError> clone(mtcore::Allocator &allocator, const size_t newLen) {
205 ensure(bits.head, "BITSET NOT INITIALIZED");
206 Bitset res;
207 auto initRes = res.init(allocator, newLen);
208 if (initRes.is_error()) {
209 return initRes.error();
210 }
211 for (size_t i = 0; i < res.bits.len && i < bits.len; ++i) {
212 res.bits[i] = bits[i];
213 }
214 return res;
215 }
216
217 Result<void, AllocationError> resize(Allocator &alloc, const size_t newLen, const bool newVal = false) {
218 ensure(bits.head, "BITSET NOT INITIALIZED");
219 Bitset res;
220 auto initRes = res.init(alloc, newLen);
221
222 if (initRes.is_error()) {
223 return initRes.error();
224 }
225
226 // by setting all, any new chunks will have the new value set
227 if (newVal) {
228 res.set_all(true);
229 }
230
231 // Copy the old bits over, this will overwrite any existing set bits
232 for (size_t i = 0; i < res.bits.len && i < bits.len; ++i) {
233 res.bits[i] = bits[i];
234 }
235
236 if (newVal) {
237 // For the last segment copied over, just set the new bits individually
238 for (size_t i = (bits.len - 1) * 64; i < newLen && i < bits.len * 64; ++i) {
239 set(i, true);
240 }
241 }
242
243 // Now that we have our copy, do a swap
244 std::swap(bits, res.bits);
245 std::swap(len, res.len);
246
247 // Finally, clean up the old bits
248 res.deinit(alloc);
249 return success();
250 }
251
257 Bitset &set(const size_t pos, const bool val) {
258 ensure(bits.head, "BITSET NOT INITIALIZED");
259 ensure(pos < len, "OUT OF BOUNDS ACCESS");
260 const u64 block = pos / 64;
261 const u64 bit = pos % 64;
262 ensure(bit + block * 64 == pos, 'BAD POSITION DECONSTRUCT');
263 const u64 bitMask = static_cast<u64>(0x1) << bit;
264 if (val) {
265 bits[block] |= bitMask;
266 }
267 else {
268 bits[block] &= ~bitMask;
269 }
270 return *this;
271 }
272
278 Bitset &toggle(const size_t pos) {
279 ensure(bits.head, "BITSET NOT INITIALIZED");
280 ensure(pos < len, "OUT OF BOUNDS ACCESS");
281 const u64 block = pos / 64;
282 const u64 bit = pos % 64;
283 const u64 bitMask = 0x1 << bit;
284 bits[block] ^= bitMask;
285 return *this;
286 }
287
293 [[nodiscard]] Optional<size_t> first_set() const {
294 ensure(bits.head, "BITSET NOT INITIALIZED");
295 for (size_t i = 0; i < bits.len; ++i) {
296 if (bits[i]) {
297 return 64 * i + std::countr_zero(bits[i]);
298 }
299 }
300 return nullopt;
301 }
302
307 [[nodiscard]] size_t pop_count() const {
308 ensure(bits.head, "BITSET NOT INITIALIZED");
309 size_t count = 0;
310 for (size_t i = 0; i < bits.len; ++i) {
311 count += std::popcount(bits[i]);
312 }
313 return count;
314 }
315
320 struct SetBitIter {
321 const Bitset &bs;
323 size_t curIndex;
324
330 while (!curMask && ++curIndex < bs.bits.len) {
331 curMask = bs.bits[curIndex];
332 }
333 if (curIndex >= bs.bits.len) {
334 return nullopt;
335 }
336 mtdefer { curMask ^= static_cast<u64>(0x1) << std::countr_zero(curMask); };
337 const size_t res = 64 * curIndex + std::countr_zero(curMask);
338 return res;
339 }
340 };
341
346 [[nodiscard]] SetBitIter set_bits() const {
347 ensure(bits.head, "BITSET NOT INITIALIZED");
348 return SetBitIter{*this, bits[0], 0};
349 }
350
351 Bitset &operator&=(const Bitset &other) {
352 ensure(bits.head, "BITSET NOT INITIALIZED");
353 size_t i = 0;
354 for (; i < other.bits.len && i < bits.len; ++i) {
355 bits[i] &= other.bits[i];
356 }
357 for (; i < bits.len; ++i) {
358 bits[i] = 0;
359 }
360 return *this;
361 }
362
363 Bitset &operator|=(const Bitset &other) {
364 ensure(bits.head, "BITSET NOT INITIALIZED");
365 for (size_t i = 0; i < other.bits.len && i < bits.len; ++i) {
366 bits[i] |= other.bits[i];
367 }
368 return *this;
369 }
370
371 Bitset &operator^=(const Bitset &other) {
372 ensure(bits.head, "BITSET NOT INITIALIZED");
373 for (size_t i = 0; i < other.bits.len && i < bits.len; ++i) {
374 bits[i] ^= other.bits[i];
375 }
376 return *this;
377 }
378
383 void set_all(const bool value) {
384 ensure(bits.head, "BITSET NOT INITIALIZED");
385 const u64 mask = value ? std::numeric_limits<u64>::max() : 0;
386
387 // For the first n 64-bit segments, just set them all
388 for (size_t i = 0; i < bits.len - 1; ++i) {
389 bits[i] = mask;
390 }
391
392 // For the last segment, just set what we have allocated
393 for (size_t i = (bits.len - 1) * 64; i < size(); ++i) {
394 set(i, value);
395 }
396 }
397
398 Bitset &operator<<=(const size_t amount) {
399 ensure(bits.head, "BITSET NOT INITIALIZED");
400 const auto blockShift = amount / 64;
401 const auto bitShift = amount % 64;
402 if (blockShift > bits.len) {
403 set_all(false);
404 }
405 else {
406 for (size_t i = 0; i < bits.len - blockShift; ++i) {
407 const auto finalIndex = bits.len - i - 1;
408 const auto sourceIndex = bits.len - i - 1 - blockShift;
409 bits[finalIndex] = bits[sourceIndex];
410 }
411 for (size_t i = 0; i < blockShift; ++i) {
412 bits[i] = 0;
413 }
414
415 constexpr u64 fullMask = std::numeric_limits<u64>::max();
416 if (bitShift) {
417 const u64 keepMask = fullMask >> bitShift;
418 const u64 carryMask = ~keepMask;
419
420 u64 carry = 0;
421 for (size_t i = 0; i < bits.len; ++i) {
422 const auto newCarry = (bits[i] & carryMask) >> (64 - bitShift);
423 bits[i] = ((bits[i] & keepMask) << bitShift) | carry;
424 carry = newCarry;
425 }
426 }
427
428 const u64 finalMask = fullMask >> (len % 64);
429 bits[bits.len - 1] &= finalMask;
430 }
431 return *this;
432 }
433
434 Bitset &operator>>=(const size_t amount) {
435 ensure(bits.head, "BITSET NOT INITIALIZED");
436 const auto blockShift = amount / 64;
437 const auto bitShift = amount % 64;
438 if (blockShift > bits.len) {
439 set_all(false);
440 }
441 else {
442 for (size_t i = 0; i < bits.len - blockShift; ++i) {
443 const auto finalIndex = i;
444 const auto sourceIndex = i + blockShift;
445 bits[finalIndex] = bits[sourceIndex];
446 }
447 for (size_t i = bits.len - blockShift; i < bits.len; ++i) {
448 bits[i] = 0;
449 }
450
451 const u64 fullMask = std::numeric_limits<u64>::max();
452 if (bitShift) {
453 const u64 keepMask = fullMask << bitShift;
454 const u64 carryMask = ~keepMask;
455
456 u64 carry = 0;
457 for (size_t i = 0; i < bits.len; ++i) {
458 const auto newCarry = (bits[i] & carryMask) << (64 - bitShift);
459 bits[i] = ((bits[i] & keepMask) >> bitShift) | carry;
460 carry = newCarry;
461 }
462 }
463
464 const u64 finalMask = fullMask << (len % 64);
465 bits[bits.len - 1] &= finalMask;
466 }
467 return *this;
468 }
469 };
470
477 struct Bitset2D {
479 size_t width;
480 size_t height;
481
489 Result<void, AllocationError> init(mtcore::Allocator &allocator, const size_t height, const size_t width) {
490 ensure(width, "INVALID WIDTH");
491 ensure(height, "INVALID HEIGHT");
492 auto baseRes = underlying.init(allocator, width * height);
493 if (baseRes.is_error()) {
494 return baseRes;
495 }
496
497 ensure(underlying.size() == width * height, "BAD UNDERLYING SIZE!");
498 this->width = width;
499 this->height = height;
500 return success();
501 }
502
509 ensure(binaryStr.head && binaryStr.len && binaryStr[0].head && binaryStr[0].len, "INVALID BINARY STRING");
510 const size_t max_width = binaryStr[0].len;
511 for (size_t i = 1; i < binaryStr.len; ++i) {
512 ensure(binaryStr[i].head && binaryStr[i].len, "INVALID BINARY STRING ROW");
513 }
514 return init(allocator, binaryStr.len, max_width, binaryStr);
515 }
516
523 return init(allocator, binaryStr.to_const());
524 }
525
534 Result<void, BitStrInitError> init(mtcore::Allocator &allocator, const size_t height, const size_t width,
535 const Slice<Slice<const char>> binaryStr) {
536 return init(allocator, height, width, binaryStr.to_const());
537 }
538
547 Result<void, BitStrInitError> init(mtcore::Allocator &allocator, const size_t height, const size_t width,
548 const Slice<const Slice<const char>> binaryStr) {
549 {
550 Slice<const char> curRow;
551 auto rowIter = binaryStr.iter();
552 while (rowIter.next().copy_if_present(curRow)) {
553 char curCell = '\0';
554 auto cellIter = curRow.iter();
555 while (cellIter.next().copy_if_present(curCell)) {
556 if (curCell != '1' && curCell != '0') {
558 }
559 }
560 }
561 }
562
563 if (const auto baseRes = init(allocator, height, width); baseRes.is_error()) {
565 }
566
567 Slice<const char> curRow;
568 auto rowIter = binaryStr.iter();
569 size_t row = 0;
570 while (rowIter.next().copy_if_present(curRow)) {
571 mtdefer { ++row; };
572 char curCell = '\0';
573 auto cellIter = curRow.iter();
574 size_t col = 0;
575 while (cellIter.next().copy_if_present(curCell)) {
576 mtdefer { ++col; };
577 if (curCell == '1') {
578 set(row, col, true);
579 }
580 }
581 }
582 return success();
583 }
584
589 void deinit(mtcore::Allocator &allocator) {
590 underlying.deinit(allocator);
591 width = 0;
592 height = 0;
593 }
594
599 std::tuple<size_t, size_t> pos;
601
602 [[nodiscard]] bool val() const { return bs.at(std::get<0>(pos), std::get<1>(pos)); }
603 [[nodiscard]] operator bool() const { return val(); }
604 BitsetAccess &set(const bool val) {
605 bs.set(std::get<0>(pos), std::get<1>(pos), val);
606 return *this;
607 }
608
609 BitsetAccess &operator=(const bool val) { return set(val); }
610 BitsetAccess &operator&=(const bool other) { return set(val() & other); }
611 BitsetAccess &operator|=(const bool other) { return set(val() | other); }
612 BitsetAccess &operator^=(const bool other) { return set(val() ^ other); }
613 };
614
620 [[nodiscard]] bool operator[](std::tuple<size_t, size_t> index) const {
621 return at(std::get<0>(index), std::get<1>(index));
622 }
623
629 [[nodiscard]] BitsetAccess operator[](std::tuple<size_t, size_t> index) {
630 ensure(std::get<0>(index) < height, "OUT OF BOUNDS ACCESS, INVALID ROW");
631 ensure(std::get<1>(index) < width, "OUT OF BOUNDS ACCESS, INVALID COL");
632 return {index, *this};
633 }
634
639 [[nodiscard]] std::tuple<size_t, size_t> size() const { return {height, width}; }
640
647 [[nodiscard]] bool at(size_t row, size_t col) const {
648 ensure(row < height, "OUT OF BOUNDS ACCESS, INVALID ROW");
649 ensure(col < width, "OUT OF BOUNDS ACCESS, INVALID COL");
650 const auto pos = row * width + col;
651 return underlying.at(pos);
652 }
653
660 auto copyRes = underlying.clone(allocator);
661 if (copyRes.is_success()) {
662 return copyRes.error();
663 }
664 auto cpy = *this;
665 cpy.underlying = *copyRes;
666 return cpy;
667 }
668
677 const size_t newWidth) {
678 Bitset2D res;
679 auto initRes = res.init(allocator, newHeight, newWidth);
680 if (initRes.is_error()) {
681 return initRes.error();
682 }
683 for (size_t row = 0; row < newHeight && row < height; ++row) {
684 for (size_t col = 0; col < newWidth && col < width; ++col) {
685 res[{row, col}] = at(row, col);
686 }
687 }
688 return res;
689 }
690
697 Bitset2D &set(size_t row, size_t col, bool val) {
698 ensure(row < height, "OUT OF BOUNDS ACCESS, INVALID ROW");
699 ensure(col < width, "OUT OF BOUNDS ACCESS, INVALID COL");
700 const auto pos = row * width + col;
701 underlying.set(pos, val);
702 return *this;
703 }
704
711 Bitset2D &toggle(size_t row, size_t col) {
712 ensure(row < height, "OUT OF BOUNDS ACCESS, INVALID ROW");
713 ensure(col < width, "OUT OF BOUNDS ACCESS, INVALID COL");
714 const auto pos = row * width + col;
715 underlying.toggle(pos);
716 return *this;
717 }
718
725 auto index = underlying.first_set();
726 if (index.empty()) {
727 return nullopt;
728 }
729 size_t pos = *index;
730 size_t row = pos / width;
731 size_t col = pos % width;
732 return std::tuple{row, col};
733 }
734
739 [[nodiscard]] size_t pop_count() const { return underlying.pop_count(); }
740
745 struct SetBitIter {
746 const Bitset2D &bs;
748 size_t curIndex;
749
755 while (!curMask && ++curIndex < bs.underlying.bits.len) {
756 curMask = bs.underlying.bits[curIndex];
757 }
758 if (curIndex >= bs.underlying.bits.len) {
759 return nullopt;
760 }
761 mtdefer { curMask ^= static_cast<u64>(0x1) << std::countr_zero(curMask); };
762 const size_t pos = 64 * curIndex + std::countr_zero(curMask);
763 size_t row = pos / bs.width;
764 size_t col = pos % bs.width;
765 return std::tuple{row, col};
766 }
767 };
768
773 [[nodiscard]] SetBitIter set_bits() const { return SetBitIter{*this, underlying.bits[0], 0}; }
774
775 Bitset2D &operator&=(const Bitset2D &other) {
776 size_t row = 0;
777 for (; row < other.height && row < height; ++row) {
778 size_t col = 0;
779 for (; col < other.width && col < width; ++col) {
780 this->operator[]({row, col}) &= other[{row, col}];
781 }
782 for (; col < width; ++col) {
783 set(row, col, false);
784 }
785 }
786 for (; row < height; ++row) {
787 for (size_t col = 0; col < width; ++col) {
788 set(row, col, false);
789 }
790 }
791 return *this;
792 }
793
794 Bitset2D &operator|=(const Bitset2D &other) {
795 for (size_t row = 0; row < other.height && row < height; ++row) {
796 for (size_t col = 0; col < other.width && col < width; ++col) {
797 this->operator[]({row, col}) |= other[{row, col}];
798 }
799 }
800 return *this;
801 }
802
803 Bitset2D &operator^=(const Bitset2D &other) {
804 for (size_t row = 0; row < other.height && row < height; ++row) {
805 for (size_t col = 0; col < other.width && col < width; ++col) {
806 this->operator[]({row, col}) ^= other[{row, col}];
807 }
808 }
809 return *this;
810 }
811
816 void set_all(bool value) { underlying.set_all(value); }
817 };
818
825 template<size_t NumBits = 64>
826 struct BitsetFixed {
827 std::array<u64, (NumBits + 63) / 64> bytes = {0};
829
830 void init(u64 val) {
831 bits[bits.size() - 1] = val;
832 if constexpr (NumBits % 64 != 0) {
833 *this <<= (NumBits % 64);
834 }
835 }
836
842 {
843 char cur = '\0';
844 auto iter = binaryStr.iter();
845 while (iter.next().copy_if_present(cur)) {
846 if (cur != '1' && cur != '0') {
848 }
849 }
850 }
851
852 char cur = '\0';
853 auto iter = binaryStr.iter();
854 size_t index = 0;
855 while (iter.next().copy_if_present(cur)) {
856 mtdefer { ++index; };
857 if (cur == '1') {
858 set(index, true);
859 }
860 }
861 return success();
862 }
863
868 size_t pos;
870
871 [[nodiscard]] operator bool() const { return bs.at(pos); }
872 [[nodiscard]] bool val() const { return bs.at(pos); }
873 BitsetAccess &set(const bool newVal) {
874 bs.set(pos, newVal);
875 return *this;
876 }
877
878 BitsetAccess &operator=(const bool val) { return set(val); }
879 BitsetAccess &operator&=(const bool other) { return set(val() & other); }
880 BitsetAccess &operator|=(const bool other) { return set(val() | other); }
881 BitsetAccess &operator^=(const bool other) { return set(val() ^ other); }
882 };
883
889 [[nodiscard]] BitsetAccess operator[](const size_t index) {
890 ensure(index < NumBits, "OUT OF BOUNDS ACCESS");
891 return {.pos = index, .bs = *this};
892 }
893
899 [[nodiscard]] bool operator[](const size_t index) const { return at(index); }
900
905 [[nodiscard]] constexpr size_t size() const { return NumBits; }
906
912 [[nodiscard]] bool at(const size_t pos) const {
913 ensure(pos < NumBits, "OUT OF BOUNDS ACCESS");
914 const auto block = pos / 64;
915 const auto bit = pos % 64;
916 const u64 bitMask = static_cast<u64>(0x1) << bit;
917 return (bits[block] & bitMask) != 0;
918 }
919
925 BitsetFixed &set(const size_t pos, const bool val) {
926 ensure(pos < NumBits, "OUT OF BOUNDS ACCESS");
927 const u64 block = pos / 64;
928 const u64 bit = pos % 64;
929 ensure(bit + block * 64 == pos, 'BAD POSITION DECONSTRUCT');
930 const u64 bitMask = static_cast<u64>(0x1) << bit;
931 if (val) {
932 bits[block] |= bitMask;
933 }
934 else {
935 bits[block] &= ~bitMask;
936 }
937 return *this;
938 }
939
945 Bitset &toggle(const size_t pos) {
946 ensure(pos < NumBits, "OUT OF BOUNDS ACCESS");
947 const u64 block = pos / 64;
948 const u64 bit = pos % 64;
949 const u64 bitMask = 0x1 << bit;
950 bits[block] ^= bitMask;
951 return *this;
952 }
953
959 [[nodiscard]] Optional<size_t> last_set() const {
960 for (size_t i = bits.len; i > 0; --i) {
961 auto indx = i - 1;
962 if (bits[indx]) {
963 return 64 * indx + 63 - std::countl_zero(bits[indx]);
964 }
965 }
966 return nullopt;
967 }
968
974 [[nodiscard]] Optional<size_t> first_set() const {
975 for (size_t i = 0; i < bits.len; ++i) {
976 if (bits[i]) {
977 return 64 * i + std::countr_zero(bits[i]);
978 }
979 }
980 return nullopt;
981 }
982
987 [[nodiscard]] size_t pop_count() const {
988 size_t count = 0;
989 for (size_t i = 0; i < bits.len; ++i) {
990 count += std::popcount(bits[i]);
991 }
992 return count;
993 }
994
999 struct SetBitIter {
1002 size_t curIndex;
1003
1009 while (!curMask && ++curIndex < bs.bits.len) {
1010 curMask = bs.bits[curIndex];
1011 }
1012 if (curIndex >= bs.bits.len) {
1013 return nullopt;
1014 }
1015 mtdefer { curMask ^= static_cast<u64>(0x1) << std::countr_zero(curMask); };
1016 const size_t res = 64 * curIndex + std::countr_zero(curMask);
1017 return res;
1018 }
1019 };
1020
1025 [[nodiscard]] SetBitIter set_bits() const { return SetBitIter{*this, bits[0], 0}; }
1026
1028 size_t i = 0;
1029 for (; i < other.bits.len && i < bits.len; ++i) {
1030 bits[i] &= other.bits[i];
1031 }
1032 for (; i < bits.len; ++i) {
1033 bits[i] = 0;
1034 }
1035 return *this;
1036 }
1037
1039 for (size_t i = 0; i < other.bits.len && i < bits.len; ++i) {
1040 bits[i] |= other.bits[i];
1041 }
1042 return *this;
1043 }
1044
1046 for (size_t i = 0; i < other.bits.len && i < bits.len; ++i) {
1047 bits[i] ^= other.bits[i];
1048 }
1049 return *this;
1050 }
1051
1056 void set_all(const bool value) {
1057 const u64 mask = value ? std::numeric_limits<u64>::max() : 0;
1058
1059 // For the first n 64-bit segments, just set them all
1060 for (size_t i = 0; i < bits.len - 1; ++i) {
1061 bits[i] = mask;
1062 }
1063
1064 // For the last segment, just set what we have allocated
1065 for (size_t i = (bits.len - 1) * 64; i < size(); ++i) {
1066 set(i, value);
1067 }
1068 }
1069
1070 BitsetFixed &operator<<=(const size_t amount) {
1071 const auto blockShift = amount / 64;
1072 const auto bitShift = amount % 64;
1073 if (blockShift > bits.len) {
1074 set_all(false);
1075 }
1076 else {
1077 for (size_t i = 0; i < bits.len - blockShift; ++i) {
1078 const auto finalIndex = bits.len - i - 1;
1079 const auto sourceIndex = bits.len - i - 1 - blockShift;
1080 bits[finalIndex] = bits[sourceIndex];
1081 }
1082 for (size_t i = 0; i < blockShift; ++i) {
1083 bits[i] = 0;
1084 }
1085
1086 constexpr u64 fullMask = std::numeric_limits<u64>::max();
1087 if (bitShift) {
1088 const u64 keepMask = fullMask >> bitShift;
1089 const u64 carryMask = ~keepMask;
1090
1091 u64 carry = 0;
1092 for (size_t i = 0; i < bits.len; ++i) {
1093 const auto newCarry = (bits[i] & carryMask) >> (64 - bitShift);
1094 bits[i] = ((bits[i] & keepMask) << bitShift) | carry;
1095 carry = newCarry;
1096 }
1097 }
1098
1099 const u64 finalMask = fullMask >> (NumBits % 64);
1100 bits[bits.len - 1] &= finalMask;
1101 }
1102 return *this;
1103 }
1104
1105 BitsetFixed &operator>>=(const size_t amount) {
1106 const auto blockShift = amount / 64;
1107 const auto bitShift = amount % 64;
1108 if (blockShift > bits.len) {
1109 set_all(false);
1110 }
1111 else {
1112 for (size_t i = 0; i < bits.len - blockShift; ++i) {
1113 const auto finalIndex = i;
1114 const auto sourceIndex = i + blockShift;
1115 bits[finalIndex] = bits[sourceIndex];
1116 }
1117 for (size_t i = bits.len - blockShift; i < bits.len; ++i) {
1118 bits[i] = 0;
1119 }
1120
1121 const u64 fullMask = std::numeric_limits<u64>::max();
1122 if (bitShift) {
1123 const u64 keepMask = fullMask << bitShift;
1124 const u64 carryMask = ~keepMask;
1125
1126 u64 carry = 0;
1127 for (size_t i = 0; i < bits.len; ++i) {
1128 const auto newCarry = (bits[i] & carryMask) << (64 - bitShift);
1129 bits[i] = ((bits[i] & keepMask) >> bitShift) | carry;
1130 carry = newCarry;
1131 }
1132 }
1133
1134 const u64 finalMask = fullMask << (NumBits % 64);
1135 bits[bits.len - 1] &= finalMask;
1136 }
1137 return *this;
1138 }
1139 };
1140
1141} // namespace mtcore
1142#endif // MTCORE_BITSET_HPP
constexpr Slice< char32_t > mut_slice_from(char32_t *cstr, size_t len)
Creates a mutable slice from a utf32 string and length.
constexpr auto nullopt
Placeholder value for an empty Optional.
Definition optional.hpp:409
BitStrInitError
Bitset String-based Initialization Errors.
Definition bitset.hpp:35
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
#define mtdefer
Defer statement that will mtdefer execution until the scope is left, at which point the code will run...
Success< void > success()
Creates a successful void Result object.
Definition result.hpp:398
Error< Underlying > error(Underlying err)
Creates an error.
Definition result.hpp:425
uint64_t u64
Alias for 64-bit unsigned ints.
Generic iterator defaults built on common contracts Does not guarantee performance of iterators Actua...
Definition iter.hpp:91
Core library for C++ with Zig-related functionality.
Represents a memory allocator Exact behavior depends on the underlying VTable used Should use the a_*...
void destroy_many(Slice< T > s)
Destroys objects allocated by this allocator by calling the destructors and freeing memory.
Result< Slice< T >, AllocationError > create_many(size_t count=1)
Allocates some block of memory with an allocator with a known type Will call default constructor on t...
Helper for accessing a bitset position (including for setting bits)
Definition bitset.hpp:598
BitsetAccess & set(const bool val)
Definition bitset.hpp:604
BitsetAccess & operator|=(const bool other)
Definition bitset.hpp:611
BitsetAccess & operator^=(const bool other)
Definition bitset.hpp:612
BitsetAccess & operator=(const bool val)
Definition bitset.hpp:609
std::tuple< size_t, size_t > pos
Definition bitset.hpp:599
BitsetAccess & operator&=(const bool other)
Definition bitset.hpp:610
Simple bit set iterator that uses next() iteration to return the indices of all set bits.
Definition bitset.hpp:745
Optional< std::tuple< size_t, size_t > > next()
Gets the next set bit position (if there is one)
Definition bitset.hpp:754
Represents a bitset with dynamically allocated memory (using an mtcore allocator) Allows operating on...
Definition bitset.hpp:477
Result< Bitset2D, AllocationError > clone(mtcore::Allocator &allocator, const size_t newHeight, const size_t newWidth)
Clones and resizes a bitset using a specific allocator.
Definition bitset.hpp:676
Result< void, BitStrInitError > init(mtcore::Allocator &allocator, const Slice< Slice< const char > > binaryStr)
Initializes a bitset from a binary string of 1's and 0's.
Definition bitset.hpp:522
bool operator[](std::tuple< size_t, size_t > index) const
Access an individual bit at a specific position.
Definition bitset.hpp:620
Bitset2D & operator|=(const Bitset2D &other)
Definition bitset.hpp:794
void set_all(bool value)
Sets all bits to a specific value.
Definition bitset.hpp:816
bool at(size_t row, size_t col) const
Reads a bit at a specific position.
Definition bitset.hpp:647
Result< void, AllocationError > init(mtcore::Allocator &allocator, const size_t height, const size_t width)
Initializes a bitset to have a total number of bits All bits will be set to zero.
Definition bitset.hpp:489
Result< Bitset2D, AllocationError > clone(mtcore::Allocator &allocator)
Clones a bitset using a specific allocator.
Definition bitset.hpp:659
Bitset2D & operator&=(const Bitset2D &other)
Definition bitset.hpp:775
BitsetAccess operator[](std::tuple< size_t, size_t > index)
Access an individual bit at a specific position.
Definition bitset.hpp:629
Bitset underlying
Definition bitset.hpp:478
Result< void, BitStrInitError > init(mtcore::Allocator &allocator, const Slice< const Slice< const char > > binaryStr)
Initializes a bitset from a binary string of 1's and 0's.
Definition bitset.hpp:508
size_t pop_count() const
Counts the number of set bits in the bitset.
Definition bitset.hpp:739
void deinit(mtcore::Allocator &allocator)
Deinitialies a bitset.
Definition bitset.hpp:589
SetBitIter set_bits() const
Iterates over the indices of all set bits in the bitset.
Definition bitset.hpp:773
Result< void, BitStrInitError > init(mtcore::Allocator &allocator, const size_t height, const size_t width, const Slice< Slice< const char > > binaryStr)
Initializes a bitset of a given size from a string Any bits not set by the string will be zero.
Definition bitset.hpp:534
Bitset2D & set(size_t row, size_t col, bool val)
Sets a bit at a position to a specific value.
Definition bitset.hpp:697
std::tuple< size_t, size_t > size() const
Gets the size of the bitset.
Definition bitset.hpp:639
Optional< std::tuple< size_t, size_t > > first_set() const
Returns the position of the first set bit (if one is set) Otherwise, returns a null Optional.
Definition bitset.hpp:724
Bitset2D & toggle(size_t row, size_t col)
Toggles a bit at a specific position.
Definition bitset.hpp:711
Bitset2D & operator^=(const Bitset2D &other)
Definition bitset.hpp:803
Result< void, BitStrInitError > init(mtcore::Allocator &allocator, const size_t height, const size_t width, const Slice< const Slice< const char > > binaryStr)
Initializes a bitset of a given size from a string Any bits not set by the string will be zero.
Definition bitset.hpp:547
Helper for accessing a bitset position (including for setting bits)
Definition bitset.hpp:867
BitsetAccess & operator&=(const bool other)
Definition bitset.hpp:879
BitsetAccess & set(const bool newVal)
Definition bitset.hpp:873
BitsetAccess & operator=(const bool val)
Definition bitset.hpp:878
BitsetAccess & operator|=(const bool other)
Definition bitset.hpp:880
BitsetAccess & operator^=(const bool other)
Definition bitset.hpp:881
Simple bit set iterator that uses next() iteration to return the indices of all set bits.
Definition bitset.hpp:999
Optional< size_t > next()
Gets the next set bit position (if there is one)
Definition bitset.hpp:1008
Represents a bitset with dynamically allocated memory (using an mtcore allocator) Allows operating on...
Definition bitset.hpp:826
bool operator[](const size_t index) const
Access an individual bit at a specific position.
Definition bitset.hpp:899
SetBitIter set_bits() const
Iterates over the indices of all set bits in the bitset.
Definition bitset.hpp:1025
Slice< u64 > bits
Definition bitset.hpp:828
Optional< size_t > first_set() const
Returns the position of the first set bit (if one is set) Otherwise, returns a null Optional.
Definition bitset.hpp:974
BitsetAccess operator[](const size_t index)
Access an individual bit at a specific position.
Definition bitset.hpp:889
Bitset & toggle(const size_t pos)
Toggles a bit at a specific position.
Definition bitset.hpp:945
BitsetFixed & set(const size_t pos, const bool val)
Sets a bit at a position to a specific value.
Definition bitset.hpp:925
Result< void, BitStrInitError > init(const Slice< const char > binaryStr)
Initializes a bitset from a binary string of 1's and 0's.
Definition bitset.hpp:841
size_t pop_count() const
Counts the number of set bits in the bitset.
Definition bitset.hpp:987
void set_all(const bool value)
Sets all bits to a specific value.
Definition bitset.hpp:1056
Optional< size_t > last_set() const
Returns the position of the last set bit (if one is set) Otherwise, returns a null Optional.
Definition bitset.hpp:959
bool at(const size_t pos) const
Reads a bit at a specific position.
Definition bitset.hpp:912
void init(u64 val)
Definition bitset.hpp:830
BitsetFixed & operator|=(const BitsetFixed &other)
Definition bitset.hpp:1038
std::array< u64,(NumBits+63)/64 > bytes
Definition bitset.hpp:827
BitsetFixed & operator>>=(const size_t amount)
Definition bitset.hpp:1105
BitsetFixed & operator&=(const BitsetFixed &other)
Definition bitset.hpp:1027
BitsetFixed & operator<<=(const size_t amount)
Definition bitset.hpp:1070
BitsetFixed & operator^=(const BitsetFixed &other)
Definition bitset.hpp:1045
constexpr size_t size() const
Gets the size of the bitset.
Definition bitset.hpp:905
Helper for accessing a bitset position (including for setting bits)
Definition bitset.hpp:132
BitsetAccess & operator&=(const bool other)
Definition bitset.hpp:144
BitsetAccess & operator^=(const bool other)
Definition bitset.hpp:146
BitsetAccess & operator|=(const bool other)
Definition bitset.hpp:145
BitsetAccess & set(const bool newVal)
Definition bitset.hpp:138
BitsetAccess & operator=(const bool val)
Definition bitset.hpp:143
Simple bit set iterator that uses next() iteration to return the indices of all set bits.
Definition bitset.hpp:320
Optional< size_t > next()
Gets the next set bit position (if there is one)
Definition bitset.hpp:329
Represents a bitset with dynamically allocated memory (using an mtcore allocator) Allows operating on...
Definition bitset.hpp:46
Bitset & toggle(const size_t pos)
Toggles a bit at a specific position.
Definition bitset.hpp:278
Result< void, BitStrInitError > init(mtcore::Allocator &allocator, const Slice< const char > binaryStr)
Initializes a bitset from a binary string of 1's and 0's.
Definition bitset.hpp:76
Bitset & operator&=(const Bitset &other)
Definition bitset.hpp:351
bool at(const size_t pos) const
Reads a bit at a specific position.
Definition bitset.hpp:179
Optional< size_t > first_set() const
Returns the position of the first set bit (if one is set) Otherwise, returns a null Optional.
Definition bitset.hpp:293
Result< Bitset, AllocationError > clone(mtcore::Allocator &allocator, const size_t newLen)
Clones and resizes a bitset using a specific allocator.
Definition bitset.hpp:204
Result< Bitset, AllocationError > clone(mtcore::Allocator &allocator)
Clones a bitset using a specific allocator.
Definition bitset.hpp:193
size_t pop_count() const
Counts the number of set bits in the bitset.
Definition bitset.hpp:307
void deinit(mtcore::Allocator &allocator)
Deinitialies a bitset.
Definition bitset.hpp:122
SetBitIter set_bits() const
Iterates over the indices of all set bits in the bitset.
Definition bitset.hpp:346
Bitset & operator|=(const Bitset &other)
Definition bitset.hpp:363
size_t size() const
Gets the size of the bitset.
Definition bitset.hpp:172
Bitset & set(const size_t pos, const bool val)
Sets a bit at a position to a specific value.
Definition bitset.hpp:257
Bitset & operator>>=(const size_t amount)
Definition bitset.hpp:434
Result< void, BitStrInitError > init(mtcore::Allocator &allocator, const size_t total, const Slice< const char > binaryStr)
Initializes a bitset of a given size from a string Any bits not set by the string will be zero.
Definition bitset.hpp:88
void set_all(const bool value)
Sets all bits to a specific value.
Definition bitset.hpp:383
Slice< u64 > bits
Definition bitset.hpp:47
BitsetAccess operator[](const size_t index)
Access an individual bit at a specific position.
Definition bitset.hpp:154
Result< void, AllocationError > resize(Allocator &alloc, const size_t newLen, const bool newVal=false)
Definition bitset.hpp:217
bool operator[](const size_t index) const
Access an individual bit at a specific position.
Definition bitset.hpp:165
Bitset & operator^=(const Bitset &other)
Definition bitset.hpp:371
Bitset & operator<<=(const size_t amount)
Definition bitset.hpp:398
Result< void, AllocationError > init(mtcore::Allocator &allocator, const size_t total)
Initializes a bitset to have a total number of bits All bits will be set to zero.
Definition bitset.hpp:56
Represents a value that may or may not exist (an "Optional" value) Similar concept to std::optional,...
Definition optional.hpp:235
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
Definition result.hpp:170
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
decltype(auto) iter() const noexcept