21#ifndef MTCORE_BITSET_HPP
22#define MTCORE_BITSET_HPP
59 const auto sliceLen = total / 64 + (total % 64 ? 1 : 0);
60 ensure(sliceLen > 0,
"CANNOT CREATE EMPTY BITSET");
63 if (allocBitsRes.is_error()) {
64 return allocBitsRes.error();
67 memset(
bits.head, 0, sliceLen *
sizeof(
u64));
78 return init(allocator, binaryStr.
len, binaryStr);
94 while (
iter.next().copy_if_present(cur)) {
95 if (cur !=
'1' && cur !=
'0') {
101 auto initRes =
init(allocator, total);
102 if (initRes.is_error()) {
109 while (
iter.next().copy_if_present(cur)) {
123 if (
bits.head !=
nullptr) {
136 [[nodiscard]]
operator bool()
const {
return bs.at(
pos); }
137 [[nodiscard]]
bool val()
const {
return bs.at(
pos); }
156 ensure(index <
len,
"OUT OF BOUNDS ACCESS");
157 return {.pos = index, .bs = *
this};
165 [[nodiscard]]
bool operator[](
const size_t index)
const {
return at(index); }
172 [[nodiscard]]
size_t size()
const {
return len; }
179 [[nodiscard]]
bool at(
const size_t pos)
const {
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;
207 auto initRes = res.
init(allocator, newLen);
208 if (initRes.is_error()) {
209 return initRes.error();
211 for (
size_t i = 0; i < res.
bits.
len && i <
bits.len; ++i) {
220 auto initRes = res.
init(alloc, newLen);
222 if (initRes.is_error()) {
223 return initRes.error();
232 for (
size_t i = 0; i < res.
bits.
len && i <
bits.len; ++i) {
238 for (
size_t i = (
bits.len - 1) * 64; i < newLen && i <
bits.len * 64; ++i) {
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;
265 bits[block] |= bitMask;
268 bits[block] &= ~bitMask;
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;
295 for (
size_t i = 0; i <
bits.len; ++i) {
297 return 64 * i + std::countr_zero(
bits[i]);
310 for (
size_t i = 0; i <
bits.len; ++i) {
311 count += std::popcount(
bits[i]);
357 for (; i <
bits.len; ++i) {
365 for (
size_t i = 0; i < other.
bits.
len && i <
bits.len; ++i) {
373 for (
size_t i = 0; i < other.
bits.
len && i <
bits.len; ++i) {
385 const u64 mask = value ? std::numeric_limits<u64>::max() : 0;
388 for (
size_t i = 0; i <
bits.len - 1; ++i) {
393 for (
size_t i = (
bits.len - 1) * 64; i <
size(); ++i) {
400 const auto blockShift = amount / 64;
401 const auto bitShift = amount % 64;
402 if (blockShift >
bits.len) {
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];
411 for (
size_t i = 0; i < blockShift; ++i) {
415 constexpr u64 fullMask = std::numeric_limits<u64>::max();
417 const u64 keepMask = fullMask >> bitShift;
418 const u64 carryMask = ~keepMask;
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;
428 const u64 finalMask = fullMask >> (
len % 64);
436 const auto blockShift = amount / 64;
437 const auto bitShift = amount % 64;
438 if (blockShift >
bits.len) {
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];
447 for (
size_t i =
bits.len - blockShift; i <
bits.len; ++i) {
451 const u64 fullMask = std::numeric_limits<u64>::max();
453 const u64 keepMask = fullMask << bitShift;
454 const u64 carryMask = ~keepMask;
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;
464 const u64 finalMask = fullMask << (
len % 64);
493 if (baseRes.is_error()) {
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");
514 return init(allocator, binaryStr.len, max_width, binaryStr);
523 return init(allocator, binaryStr.to_const());
551 auto rowIter = binaryStr.
iter();
552 while (rowIter.next().copy_if_present(curRow)) {
554 auto cellIter = curRow.
iter();
555 while (cellIter.next().copy_if_present(curCell)) {
556 if (curCell !=
'1' && curCell !=
'0') {
563 if (
const auto baseRes =
init(allocator,
height,
width); baseRes.is_error()) {
568 auto rowIter = binaryStr.
iter();
570 while (rowIter.next().copy_if_present(curRow)) {
573 auto cellIter = curRow.
iter();
575 while (cellIter.next().copy_if_present(curCell)) {
577 if (curCell ==
'1') {
599 std::tuple<size_t, size_t>
pos;
602 [[nodiscard]]
bool val()
const {
return bs.at(std::get<0>(
pos), std::get<1>(
pos)); }
603 [[nodiscard]]
operator bool()
const {
return val(); }
620 [[nodiscard]]
bool operator[](std::tuple<size_t, size_t> index)
const {
621 return at(std::get<0>(index), std::get<1>(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};
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;
661 if (copyRes.is_success()) {
662 return copyRes.error();
665 cpy.underlying = *copyRes;
677 const size_t newWidth) {
679 auto initRes = res.
init(allocator, newHeight, newWidth);
680 if (initRes.is_error()) {
681 return initRes.error();
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);
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;
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;
730 size_t row = pos /
width;
731 size_t col = pos %
width;
732 return std::tuple{row, col};
763 size_t row = pos /
bs.width;
764 size_t col = pos %
bs.width;
765 return std::tuple{row, col};
779 for (; col < other.
width && col <
width; ++col) {
780 this->
operator[]({row, col}) &= other[{row, col}];
782 for (; col <
width; ++col) {
783 set(row, col,
false);
786 for (; row <
height; ++row) {
787 for (
size_t col = 0; col <
width; ++col) {
788 set(row, col,
false);
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}];
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}];
825 template<
size_t NumBits = 64>
832 if constexpr (NumBits % 64 != 0) {
833 *
this <<= (NumBits % 64);
845 while (
iter.next().copy_if_present(cur)) {
846 if (cur !=
'1' && cur !=
'0') {
855 while (
iter.next().copy_if_present(cur)) {
871 [[nodiscard]]
operator bool()
const {
return bs.at(
pos); }
872 [[nodiscard]]
bool val()
const {
return bs.at(
pos); }
890 ensure(index < NumBits,
"OUT OF BOUNDS ACCESS");
891 return {.pos = index, .bs = *
this};
899 [[nodiscard]]
bool operator[](
const size_t index)
const {
return at(index); }
905 [[nodiscard]]
constexpr size_t size()
const {
return NumBits; }
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;
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;
932 bits[block] |= bitMask;
935 bits[block] &= ~bitMask;
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;
960 for (
size_t i =
bits.len; i > 0; --i) {
963 return 64 * indx + 63 - std::countl_zero(
bits[indx]);
975 for (
size_t i = 0; i <
bits.len; ++i) {
977 return 64 * i + std::countr_zero(
bits[i]);
989 for (
size_t i = 0; i <
bits.len; ++i) {
990 count += std::popcount(
bits[i]);
1032 for (; i <
bits.len; ++i) {
1039 for (
size_t i = 0; i < other.
bits.
len && i <
bits.len; ++i) {
1046 for (
size_t i = 0; i < other.
bits.
len && i <
bits.len; ++i) {
1057 const u64 mask = value ? std::numeric_limits<u64>::max() : 0;
1060 for (
size_t i = 0; i <
bits.len - 1; ++i) {
1065 for (
size_t i = (
bits.len - 1) * 64; i <
size(); ++i) {
1071 const auto blockShift = amount / 64;
1072 const auto bitShift = amount % 64;
1073 if (blockShift >
bits.len) {
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];
1082 for (
size_t i = 0; i < blockShift; ++i) {
1086 constexpr u64 fullMask = std::numeric_limits<u64>::max();
1088 const u64 keepMask = fullMask >> bitShift;
1089 const u64 carryMask = ~keepMask;
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;
1099 const u64 finalMask = fullMask >> (NumBits % 64);
1106 const auto blockShift = amount / 64;
1107 const auto bitShift = amount % 64;
1108 if (blockShift >
bits.len) {
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];
1117 for (
size_t i =
bits.len - blockShift; i <
bits.len; ++i) {
1121 const u64 fullMask = std::numeric_limits<u64>::max();
1123 const u64 keepMask = fullMask << bitShift;
1124 const u64 carryMask = ~keepMask;
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;
1134 const u64 finalMask = fullMask << (NumBits % 64);
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.
BitStrInitError
Bitset String-based Initialization Errors.
#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.
Error< Underlying > error(Underlying err)
Creates an error.
uint64_t u64
Alias for 64-bit unsigned ints.
Generic iterator defaults built on common contracts Does not guarantee performance of iterators Actua...
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)
BitsetAccess & set(const bool val)
BitsetAccess & operator|=(const bool other)
BitsetAccess & operator^=(const bool other)
BitsetAccess & operator=(const bool val)
std::tuple< size_t, size_t > pos
BitsetAccess & operator&=(const bool other)
Simple bit set iterator that uses next() iteration to return the indices of all set bits.
Optional< std::tuple< size_t, size_t > > next()
Gets the next set bit position (if there is one)
Represents a bitset with dynamically allocated memory (using an mtcore allocator) Allows operating on...
Result< Bitset2D, AllocationError > clone(mtcore::Allocator &allocator, const size_t newHeight, const size_t newWidth)
Clones and resizes a bitset using a specific allocator.
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.
bool operator[](std::tuple< size_t, size_t > index) const
Access an individual bit at a specific position.
Bitset2D & operator|=(const Bitset2D &other)
void set_all(bool value)
Sets all bits to a specific value.
bool at(size_t row, size_t col) const
Reads a bit at a specific position.
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.
Result< Bitset2D, AllocationError > clone(mtcore::Allocator &allocator)
Clones a bitset using a specific allocator.
Bitset2D & operator&=(const Bitset2D &other)
BitsetAccess operator[](std::tuple< size_t, size_t > index)
Access an individual bit at a specific position.
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.
size_t pop_count() const
Counts the number of set bits in the bitset.
void deinit(mtcore::Allocator &allocator)
Deinitialies a bitset.
SetBitIter set_bits() const
Iterates over the indices of all set bits in the bitset.
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.
Bitset2D & set(size_t row, size_t col, bool val)
Sets a bit at a position to a specific value.
std::tuple< size_t, size_t > size() const
Gets the size of the bitset.
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.
Bitset2D & toggle(size_t row, size_t col)
Toggles a bit at a specific position.
Bitset2D & operator^=(const Bitset2D &other)
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.
Helper for accessing a bitset position (including for setting bits)
BitsetAccess & operator&=(const bool other)
BitsetAccess & set(const bool newVal)
BitsetAccess & operator=(const bool val)
BitsetAccess & operator|=(const bool other)
BitsetAccess & operator^=(const bool other)
Simple bit set iterator that uses next() iteration to return the indices of all set bits.
Optional< size_t > next()
Gets the next set bit position (if there is one)
Represents a bitset with dynamically allocated memory (using an mtcore allocator) Allows operating on...
bool operator[](const size_t index) const
Access an individual bit at a specific position.
SetBitIter set_bits() const
Iterates over the indices of all set bits in the bitset.
Optional< size_t > first_set() const
Returns the position of the first set bit (if one is set) Otherwise, returns a null Optional.
BitsetAccess operator[](const size_t index)
Access an individual bit at a specific position.
Bitset & toggle(const size_t pos)
Toggles a bit at a specific position.
BitsetFixed & set(const size_t pos, const bool val)
Sets a bit at a position to a specific value.
Result< void, BitStrInitError > init(const Slice< const char > binaryStr)
Initializes a bitset from a binary string of 1's and 0's.
size_t pop_count() const
Counts the number of set bits in the bitset.
void set_all(const bool value)
Sets all bits to a specific value.
Optional< size_t > last_set() const
Returns the position of the last set bit (if one is set) Otherwise, returns a null Optional.
bool at(const size_t pos) const
Reads a bit at a specific position.
BitsetFixed & operator|=(const BitsetFixed &other)
std::array< u64,(NumBits+63)/64 > bytes
BitsetFixed & operator>>=(const size_t amount)
BitsetFixed & operator&=(const BitsetFixed &other)
BitsetFixed & operator<<=(const size_t amount)
BitsetFixed & operator^=(const BitsetFixed &other)
constexpr size_t size() const
Gets the size of the bitset.
Helper for accessing a bitset position (including for setting bits)
BitsetAccess & operator&=(const bool other)
BitsetAccess & operator^=(const bool other)
BitsetAccess & operator|=(const bool other)
BitsetAccess & set(const bool newVal)
BitsetAccess & operator=(const bool val)
Simple bit set iterator that uses next() iteration to return the indices of all set bits.
Optional< size_t > next()
Gets the next set bit position (if there is one)
Represents a bitset with dynamically allocated memory (using an mtcore allocator) Allows operating on...
Bitset & toggle(const size_t pos)
Toggles a bit at a specific position.
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.
Bitset & operator&=(const Bitset &other)
bool at(const size_t pos) const
Reads a bit at a specific position.
Optional< size_t > first_set() const
Returns the position of the first set bit (if one is set) Otherwise, returns a null Optional.
Result< Bitset, AllocationError > clone(mtcore::Allocator &allocator, const size_t newLen)
Clones and resizes a bitset using a specific allocator.
Result< Bitset, AllocationError > clone(mtcore::Allocator &allocator)
Clones a bitset using a specific allocator.
size_t pop_count() const
Counts the number of set bits in the bitset.
void deinit(mtcore::Allocator &allocator)
Deinitialies a bitset.
SetBitIter set_bits() const
Iterates over the indices of all set bits in the bitset.
Bitset & operator|=(const Bitset &other)
size_t size() const
Gets the size of the bitset.
Bitset & set(const size_t pos, const bool val)
Sets a bit at a position to a specific value.
Bitset & operator>>=(const size_t amount)
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.
void set_all(const bool value)
Sets all bits to a specific value.
BitsetAccess operator[](const size_t index)
Access an individual bit at a specific position.
Result< void, AllocationError > resize(Allocator &alloc, const size_t newLen, const bool newVal=false)
bool operator[](const size_t index) const
Access an individual bit at a specific position.
Bitset & operator^=(const Bitset &other)
Bitset & operator<<=(const size_t amount)
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.
Represents a value that may or may not exist (an "Optional" value) Similar concept to std::optional,...
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
decltype(auto) iter() const noexcept