diff --git a/examples/0013.graph/shortest_path/spfa_optimize_fastio_bitvec.cc b/examples/0013.graph/shortest_path/spfa_optimize_fastio_bitvec.cc new file mode 100644 index 000000000..2e7d2ace8 --- /dev/null +++ b/examples/0013.graph/shortest_path/spfa_optimize_fastio_bitvec.cc @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include + +struct node +{ + std::size_t to, weight; +}; + +using namespace fast_io::io; + +int main() +{ + ::fast_io::timer timer(u8"spfa_optimize_fastio_bitvec"); + fast_io::ibuf_file ibf("graph.txt"); + std::size_t m, n; + scan(ibf, m, n); + ::fast_io::vector<::fast_io::vector> graph(n); + std::size_t const average{(m / n + 1) * 13 / 10}; + for (auto &v : graph) + { + v.reserve(average); + } + for (std::size_t i{}; i != m; ++i) + { + std::size_t a, b, w; + scan(ibf, a, b, w); + graph[a].push_back({b, w}); + } + ::fast_io::vector relax(n, SIZE_MAX); + ::fast_io::bitvec occupied(n); + ::fast_io::queue queue; + occupied.set_front(); + for (queue.push(relax.front() = 0); !queue.is_empty(); queue.pop()) + { + auto front{queue.front()}; + auto minimum_weight{relax[front]}; + for (auto e : graph[front]) + { + if (minimum_weight + e.weight < relax[e.to]) + { + relax[e.to] = minimum_weight + e.weight; + if (!occupied.test(e.to)) + { + occupied.set(e.to); + queue.push(e.to); + } + } + } + occupied.reset(front); + } + fast_io::obuf_file obf("spfa.txt"); + if (relax.back() == SIZE_MAX) + { + print(obf, "no answer\n"); + } + else + { + println(obf, relax.back()); + } +} \ No newline at end of file diff --git a/fuzzing/0007.containers/bitvec/bitvec.cc b/fuzzing/0007.containers/bitvec/bitvec.cc new file mode 100644 index 000000000..0f27b8c9e --- /dev/null +++ b/fuzzing/0007.containers/bitvec/bitvec.cc @@ -0,0 +1,37 @@ +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) +{ + fast_io::bitvec bv; + + // Push all bits from input + for (size_t i = 0; i != size; ++i) + { + bv.push_back((data[i] & 1) != 0); + } + + if (bv.size() > 8) + { + // Save original bits 1..7 + bool saved[7]; + for (size_t i = 1; i != 8; ++i) + { + saved[i - 1] = bv.test(i); + } + + // Mutate front bit + bv.set_front_unchecked(true); + + // Check if bits 1..7 changed (they SHOULD NOT) + for (size_t i = 1; i != 8; ++i) + { + if (bv.test(i) != saved[i - 1]) + { + __builtin_trap(); // crash → libFuzzer finds bug + } + } + } + + return 0; +} diff --git a/include/fast_io_dsal/bitvec.h b/include/fast_io_dsal/bitvec.h new file mode 100644 index 000000000..26f4d122f --- /dev/null +++ b/include/fast_io_dsal/bitvec.h @@ -0,0 +1,53 @@ +#pragma once + +#if !defined(__cplusplus) +#error "You must be using a C++ compiler" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "impl/misc/push_macros.h" +#include "impl/misc/push_warnings.h" +#include "../fast_io_core_impl/freestanding/impl.h" +#include "../fast_io_core_impl/terminate.h" +#include "../fast_io_core_impl/intrinsics/msvc/impl.h" +#include "../fast_io_core_impl/allocation/impl.h" +#include "../fast_io_core_impl/asan_support.h" + +#if defined(_MSC_VER) && !defined(__clang__) +#include +#endif + +#include "impl/freestanding.h" +#include "impl/common.h" +#include "impl/bitvec.h" + +#if ((__STDC_HOSTED__ == 1 && (!defined(_GLIBCXX_HOSTED) || _GLIBCXX_HOSTED == 1) && \ + !defined(_LIBCPP_FREESTANDING)) || \ + defined(FAST_IO_ENABLE_HOSTED_FEATURES)) + +namespace fast_io +{ + +using bitvec = ::fast_io::containers::bitvec<::fast_io::native_global_allocator>; + +namespace tlc +{ +using bitvec = ::fast_io::containers::bitvec<::fast_io::native_thread_local_allocator>; +} // namespace tlc + +} // namespace fast_io + +#endif + +#include "impl/misc/pop_macros.h" +#include "impl/misc/pop_warnings.h" diff --git a/include/fast_io_dsal/impl/bitvec.h b/include/fast_io_dsal/impl/bitvec.h new file mode 100644 index 000000000..b1bc4a43b --- /dev/null +++ b/include/fast_io_dsal/impl/bitvec.h @@ -0,0 +1,729 @@ +#pragma once + +namespace fast_io +{ + +namespace details +{ + +struct bitvec_rep +{ + char unsigned *begin_ptr{}; + ::std::size_t curr_pos{}, end_pos{}; +}; + +} // namespace details + +namespace containers +{ + +template +class bitvec +{ +public: + using allocator_type = allocator; + using underlying_type = char unsigned; + using size_type = ::std::size_t; + using difference_type = ::std::ptrdiff_t; + using underlying_pointer = underlying_type *; + using underlying_const_pointer = underlying_type const *; + static inline constexpr size_type underlying_digits{ + ::std::numeric_limits::digits}; + ::fast_io::details::bitvec_rep imp{}; + constexpr bitvec() noexcept = default; + static inline constexpr ::std::size_t max_size() noexcept + { + constexpr ::std::size_t szmx{::std::numeric_limits<::std::size_t>::max()}; + return szmx; + } + static inline constexpr ::std::size_t max_size_bytes() noexcept + { + constexpr ::std::size_t szbytesmx{::std::numeric_limits<::std::size_t>::max() / + ::std::numeric_limits::digits}; + return szbytesmx; + } + +private: + using typed_allocator = ::fast_io::typed_generic_allocator_adapter; + inline constexpr void grow_to_new_capacity(size_type n) noexcept + { + ::std::size_t current_capacity{this->imp.end_pos}; + if constexpr (underlying_digits == 8) + { + current_capacity >>= 3u; + } + else + { + current_capacity /= underlying_digits; + } + + auto [new_begin_ptr, new_capacity] = typed_allocator::reallocate_n_at_least(this->imp.begin_ptr, current_capacity, n); + constexpr ::std::size_t mxbytes{max_size_bytes()}; + if (mxbytes < new_capacity) + { + new_capacity = mxbytes; + } + this->imp.begin_ptr = new_begin_ptr; + if constexpr (underlying_digits == 8) + { + new_capacity <<= 3u; + } + else + { + new_capacity *= underlying_digits; + } + this->imp.end_pos = new_capacity; + } + inline constexpr void grow_twice() noexcept + { + ::std::size_t current_capacity{this->imp.end_pos}; + if constexpr (underlying_digits == 8) + { + current_capacity >>= 3u; + } + else + { + current_capacity /= underlying_digits; + } + constexpr ::std::size_t mxbyteshalf{max_size() >> 1}; + if (mxbyteshalf < current_capacity) + { + ::fast_io::fast_terminate(); + } + ::std::size_t toallocate{current_capacity << 1u}; + if (current_capacity == 0) + { + toallocate = 1; + } + this->grow_to_new_capacity(toallocate); + } + inline static constexpr ::fast_io::details::bitvec_rep allocate_new(size_type n, bool zeroing) noexcept + { + size_type to_allocate_bytes{n}; + if constexpr (underlying_digits == 8) + { + to_allocate_bytes = (n >> 3u) + ((n & 3u) != 0); + } + else + { + size_type const ndivdigits{n / underlying_digits}; + size_type const nmoddigits{n % underlying_digits}; + to_allocate_bytes = ndivdigits + (nmoddigits != 0); + } + constexpr ::std::size_t mxbytes{max_size_bytes()}; + if (mxbytes < to_allocate_bytes) + { + ::fast_io::fast_terminate(); + } + auto [new_begin_ptr, new_capacity] = (zeroing ? typed_allocator::allocate_zero_at_least(n) : typed_allocator::allocate_at_least(n)); + if (mxbytes < new_capacity) + { + new_capacity = mxbytes; + } + if constexpr (underlying_digits == 8) + { + new_capacity <<= 3u; + } + else + { + new_capacity *= underlying_digits; + } + + return {new_begin_ptr, n, new_capacity}; + } + inline static constexpr ::fast_io::details::bitvec_rep clone_imp(::fast_io::details::bitvec_rep const &other) noexcept + { + auto newrep{allocate_new(other.curr_pos, false)}; + size_type n{other.curr_pos}; + size_type to_copy_bytes; + if constexpr (underlying_digits == 8) + { + to_copy_bytes = (n >> 3u) + ((n & 3u) != 0); + } + else + { + size_type const ndivdigits{n / underlying_digits}; + size_type const nmoddigits{n % underlying_digits}; + to_copy_bytes = ndivdigits + (nmoddigits != 0); + } + ::fast_io::freestanding::non_overlapped_copy_n(other.begin_ptr, to_copy_bytes, newrep.begin_ptr); + return newrep; + } + inline constexpr void destroy_bitvec() noexcept + { + auto begin_ptr{imp.begin_ptr}; + if (begin_ptr == nullptr) + { + return; + } + typed_allocator::deallocate_n(imp.begin_ptr, imp.end_pos); + } + +public: + inline constexpr bitvec(size_type n) noexcept : imp{allocate_new(n, true)} + { + } + inline constexpr bitvec(size_type n, ::fast_io::for_overwrite_t) noexcept : imp{allocate_new(n, false)} + { + } + + inline constexpr void push_back(bool v) noexcept + { + if (this->imp.curr_pos == this->imp.end_pos) [[unlikely]] + { + this->grow_twice(); + } + this->push_back_unchecked(v); + } + + inline constexpr void push_back_unchecked(bool v) noexcept + { + if constexpr (underlying_digits == 8) + { + size_type bitpos{imp.curr_pos}; + size_type byte_index{bitpos >> 3}; // bitpos / 8 + size_type bit_index{bitpos & 7}; // bitpos % 8 + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + // Branchless set/clear + byteval = (byteval & static_cast(~mask)) | + (static_cast(v) * mask); + + ++imp.curr_pos; + } + else + { + size_type bitpos{imp.curr_pos}; + size_type byte_index{bitpos / underlying_digits}; + size_type bit_index{bitpos % underlying_digits}; + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = + static_cast(1u << bit_index); + + byteval = (byteval & static_cast(~mask)) | + (static_cast(v) * mask); + + ++imp.curr_pos; + } + } + + inline constexpr bool test_unchecked(size_type pos) const noexcept + { + if constexpr (underlying_digits == 8) + { + // Compute which byte contains the bit + size_type byte_index{pos >> 3}; // pos / 8 + // Compute which bit inside that byte + size_type bit_index{pos & 7}; // pos % 8 + + underlying_type byteval{imp.begin_ptr[byte_index]}; + + // Extract the bit + return (byteval >> bit_index) & 1u; + } + else + { + // Compute which byte contains the bit + size_type byte_index{pos / underlying_digits}; + // Compute which bit inside that byte + size_type bit_index{pos % underlying_digits}; + + underlying_type byteval{imp.begin_ptr[byte_index]}; + + // Extract the bit + return (byteval >> bit_index) & 1u; + } + } + inline constexpr bool test(size_type pos) const noexcept + { + if (this->imp.curr_pos <= pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + return this->test_unchecked(pos); + } + inline constexpr void set_unchecked(size_type pos, bool value = true) noexcept + { + if constexpr (underlying_digits == 8) + { + size_type byte_index{pos >> 3}; // pos / 8 + size_type bit_index{pos & 7}; // pos % 8 + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + // Branchless set/clear + byteval = (byteval & static_cast(~mask)) | + (static_cast(value) * mask); + } + else + { + size_type byte_index{pos / underlying_digits}; + size_type bit_index{pos % underlying_digits}; + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = + static_cast(1u << bit_index); + + byteval = (byteval & static_cast(~mask)) | + (static_cast(value) * mask); + } + } + inline constexpr void set(size_type pos, bool value = true) noexcept + { + if (this->imp.curr_pos <= pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->set_unchecked(pos, value); + } + + inline constexpr void reset_unchecked(size_type pos) noexcept + { + if constexpr (underlying_digits == 8) + { + size_type byte_index{pos >> 3}; // pos / 8 + size_type bit_index{pos & 7}; // pos % 8 + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + // Clear the bit (branchless) + byteval &= static_cast(~mask); + } + else + { + size_type byte_index{pos / underlying_digits}; + size_type bit_index{pos % underlying_digits}; + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = + static_cast(1u << bit_index); + + byteval &= static_cast(~mask); + } + } + inline constexpr void reset(size_type pos) noexcept + { + if (this->imp.curr_pos <= pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->reset_unchecked(pos); + } + + inline constexpr bool pop_back_unchecked() noexcept + { + // Position of last bit (curr_pos > 0 assumed) + size_type bitpos{imp.curr_pos - 1}; + + if constexpr (underlying_digits == 8) + { + size_type byte_index{bitpos >> 3}; // bitpos / 8 + size_type bit_index{bitpos & 7}; // bitpos % 8 + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + // Read the bit before clearing it + bool old = (byteval >> bit_index) & 1u; + + // Clear the bit + byteval &= static_cast(~mask); + + --imp.curr_pos; + return old; + } + else + { + size_type byte_index{bitpos / underlying_digits}; + size_type bit_index{bitpos % underlying_digits}; + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = + static_cast(1u << bit_index); + + bool old = (byteval >> bit_index) & 1u; + + byteval &= static_cast(~mask); + + --imp.curr_pos; + return old; + } + } + + inline constexpr bool pop_back() noexcept + { + if (!(this->imp.curr_pos)) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + return this->pop_back_unchecked(); + } + + inline constexpr bool test_front_unchecked() const noexcept + { + // front bit is always at pos 0 + return (*this->imp.begin_ptr) & 1u; + } + + inline constexpr bool test_front() const noexcept + { + if (!this->imp.curr_pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + return this->test_front_unchecked(); + } + + inline constexpr void set_front_unchecked(bool value = true) noexcept + { + underlying_type &byteval = *imp.begin_ptr; + constexpr underlying_type mask = static_cast(1u); + constexpr underlying_type invmask = ~mask; + + byteval = (byteval & invmask) | (static_cast(value)); + } + inline constexpr void set_front(bool value = true) noexcept + { + if (!this->imp.curr_pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->set_front_unchecked(value); + } + + inline constexpr void reset_front_unchecked() noexcept + { + constexpr underlying_type mask = static_cast(1u); + constexpr underlying_type invmask = ~mask; + (*imp.begin_ptr) &= invmask; + } + + inline constexpr void reset_front() noexcept + { + if (!this->imp.curr_pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->reset_front_unchecked(); + } + + inline constexpr bool test_back_unchecked() const noexcept + { + // last bit is at pos curr_pos - 1 + size_type bitpos{this->imp.curr_pos - 1}; + + if constexpr (underlying_digits == 8) + { + size_type byte_index{bitpos >> 3}; // bitpos / 8 + size_type bit_index{bitpos & 7}; // bitpos % 8 + + underlying_type byteval{this->imp.begin_ptr[byte_index]}; + return (byteval >> bit_index) & 1u; + } + else + { + size_type byte_index{bitpos / underlying_digits}; + size_type bit_index{bitpos % underlying_digits}; + + underlying_type byteval{this->imp.begin_ptr[byte_index]}; + return (byteval >> bit_index) & 1u; + } + } + + inline constexpr bool test_back() const noexcept + { + if (!this->imp.curr_pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + return this->test_back_unchecked(); + } + + inline constexpr void set_back_unchecked(bool value = true) noexcept + { + size_type bitpos{this->imp.curr_pos - 1}; + + if constexpr (underlying_digits == 8) + { + size_type byte_index{bitpos >> 3}; + size_type bit_index{bitpos & 7}; + + underlying_type &byteval = this->imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + byteval = (byteval & static_cast(~mask)) | + (static_cast(value) * mask); + } + else + { + size_type byte_index{bitpos / underlying_digits}; + size_type bit_index{bitpos % underlying_digits}; + + underlying_type &byteval = this->imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + byteval = (byteval & static_cast(~mask)) | + (static_cast(value) * mask); + } + } + + inline constexpr void set_back(bool value = true) noexcept + { + if (!this->imp.curr_pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->set_back_unchecked(value); + } + + inline constexpr void reset_back_unchecked() noexcept + { + size_type bitpos{this->imp.curr_pos - 1}; + + if constexpr (underlying_digits == 8) + { + size_type byte_index{bitpos >> 3}; + size_type bit_index{bitpos & 7}; + + underlying_type &byteval = this->imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + byteval &= static_cast(~mask); + } + else + { + size_type byte_index{bitpos / underlying_digits}; + size_type bit_index{bitpos % underlying_digits}; + + underlying_type &byteval = this->imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + byteval &= static_cast(~mask); + } + } + + inline constexpr void reset_back() noexcept + { + if (!this->imp.curr_pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->reset_back_unchecked(); + } + + inline constexpr void flip_unchecked(size_type pos) noexcept + { + if constexpr (underlying_digits == 8) + { + size_type byte_index{pos >> 3}; // pos / 8 + size_type bit_index{pos & 7}; // pos % 8 + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + // Toggle the bit (branchless) + byteval ^= mask; + } + else + { + size_type byte_index{pos / underlying_digits}; + size_type bit_index{pos % underlying_digits}; + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = + static_cast(1u << bit_index); + + byteval ^= mask; + } + } + + inline constexpr void flip(size_type pos) noexcept + { + if (this->imp.curr_pos <= pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->flip_unchecked(pos); + } + + inline constexpr void flip_front_unchecked() noexcept + { + // front bit is always bit 0 of byte 0 + constexpr underlying_type mask = static_cast(1u); + (*imp.begin_ptr) ^= mask; + } + inline constexpr void flip_front() noexcept + { + if (!this->imp.curr_pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->flip_front_unchecked(); + } + + inline constexpr void flip_back_unchecked() noexcept + { + size_type bitpos{imp.curr_pos - 1}; + + if constexpr (underlying_digits == 8) + { + size_type byte_index{bitpos >> 3}; // bitpos / 8 + size_type bit_index{bitpos & 7}; // bitpos % 8 + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = static_cast(1u << bit_index); + + byteval ^= mask; + } + else + { + size_type byte_index{bitpos / underlying_digits}; + size_type bit_index{bitpos % underlying_digits}; + + underlying_type &byteval = imp.begin_ptr[byte_index]; + underlying_type mask = + static_cast(1u << bit_index); + + byteval ^= mask; + } + } + + inline constexpr void flip_back() noexcept + { + if (!this->imp.curr_pos) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->flip_back_unchecked(); + } + + constexpr bitvec(bitvec &&other) noexcept : imp{other.imp} + { + other.imp = {}; + } + constexpr bitvec &operator=(bitvec &&other) noexcept + { + if (__builtin_addressof(other) == this) [[unlikely]] + { + return *this; + } + this->destroy_bitvec(); + this->imp = other.imp; + other.imp = {}; + return *this; + } + constexpr bitvec(bitvec const &other) noexcept : imp{clone_imp(other.imp)} + {} + constexpr bitvec &operator=(bitvec const &other) noexcept + { + if (__builtin_addressof(other) == this) [[unlikely]] + { + return *this; + } + auto newimp{clone_imp(other.imp)}; + this->destroy_bitvec(); + this->imp = newimp; + return *this; + } + constexpr ~bitvec() + { + this->destroy_bitvec(); + } + + constexpr underlying_pointer underlying_data() noexcept + { + return this->imp.begin_ptr; + } + constexpr underlying_const_pointer underlying_data() const noexcept + { + return this->imp.begin_ptr; + } + constexpr size_type size() noexcept + { + return this->imp.curr_pos; + } + constexpr size_type size_bytes() const noexcept + { + size_type pos{this->imp.curr_pos}; + if constexpr (underlying_digits == 8) + { + // Compute which byte contains the bit + size_type byte_index{pos >> 3u}; // pos / 8 + // Compute which bit inside that byte + size_type bit_index{pos & 7u}; // pos % 8 + return byte_index + (bit_index != 0u); + } + else + { + // Compute which byte contains the bit + size_type byte_index{pos / underlying_digits}; + // Compute which bit inside that byte + size_type bit_index{pos % underlying_digits}; + return byte_index + (bit_index != 0u); + } + } + constexpr size_type capacity() noexcept + { + return this->imp.end_pos; + } + constexpr size_type capacity_bytes() const noexcept + { + size_type pos{this->imp.end_pos}; + if constexpr (underlying_digits == 8) + { + // Compute which byte contains the bit + size_type byte_index{pos >> 3u}; // pos / 8 + // Compute which bit inside that byte + size_type bit_index{pos & 7u}; // pos % 8 + return byte_index + (bit_index != 0u); + } + else + { + // Compute which byte contains the bit + size_type byte_index{pos / underlying_digits}; + // Compute which bit inside that byte + size_type bit_index{pos % underlying_digits}; + return byte_index + (bit_index != 0u); + } + } + constexpr void clear() noexcept + { + this->imp.curr_pos = 0; + } + constexpr void clear_destroy() noexcept + { + this->destroy_bitvec(); + this->imp = {}; + } + +private: + inline static constexpr size_type bits_to_blocks(size_type bits) noexcept + { + if constexpr (underlying_digits == 8) + { + return (bits + 7) >> 3; // ceil(bits / 8) + } + else + { + return (bits + (underlying_digits - 1)) / underlying_digits; + } + } + +public: + constexpr void reserve(size_type n) noexcept + { + if (this->imp.end_pos < n) + { + this->grow_to_new_capacity(bits_to_blocks(n)); + } + } +}; + +} // namespace containers + +} // namespace fast_io diff --git a/tests/0026.container/0014.bitvec/bitvec.cc b/tests/0026.container/0014.bitvec/bitvec.cc new file mode 100644 index 000000000..4a69deb5a --- /dev/null +++ b/tests/0026.container/0014.bitvec/bitvec.cc @@ -0,0 +1,469 @@ +#include +#include + +inline void test_bitvec_basic() +{ + ::fast_io::io::perr("=== bitvec basic test ===\n"); + + ::fast_io::bitvec bv; + + // Push 4096 alternating bits + for (::std::size_t i{}; i != 4096u; ++i) + { + bv.push_back((i & 1u) != 0u); + } + + if (bv.size() != 4096u) + { + ::fast_io::io::panic("ERROR: size mismatch after push_back\n"); + } + + // Verify alternating pattern + for (::std::size_t i{}; i != 4096u; ++i) + { + bool expected = (i & 1u) != 0u; + if (bv.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: test() mismatch at index ", i); + } + } + + ::fast_io::io::print("bitvec basic test finished\n"); +} + +inline void test_bitvec_set_reset() +{ + ::fast_io::io::perr("=== bitvec set/reset test ===\n"); + + ::fast_io::bitvec bv; + + // Push 100 bits of 0 + for (::std::size_t i{}; i != 100u; ++i) + { + bv.push_back(false); + } + + // Set even bits to 1 + for (::std::size_t i{}; i < 100u; i += 2u) + { + bv.set_unchecked(i, true); + } + + // Verify + for (::std::size_t i{}; i != 100u; ++i) + { + bool expected = (i % 2u == 0u); + if (bv.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: set_unchecked mismatch at index ", i); + } + } + + // Reset all bits + for (::std::size_t i{}; i != 100u; ++i) + { + bv.reset_unchecked(i); + } + + for (::std::size_t i{}; i != 100u; ++i) + { + if (bv.test(i) != false) + { + ::fast_io::io::panicln("ERROR: reset_unchecked mismatch at index ", i); + } + } + + ::fast_io::io::print("bitvec set/reset test finished\n"); +} + +inline void test_bitvec_front_back() +{ + ::fast_io::io::perr("=== bitvec front/back test ===\n"); + + ::fast_io::bitvec bv; + + // Push 10 bits: 0 1 0 1 0 1 0 1 0 1 + for (::std::size_t i{}; i != 10u; ++i) + { + bv.push_back((i & 1u) != 0u); + } + + // Verify front bit (should be 0) + if (bv.test_front() != false) + { + ::fast_io::io::panic("ERROR: test_front mismatch (expected 0)\n"); + } + + // Verify back bit (should be 1) + if (bv.test_back() != true) + { + ::fast_io::io::panic("ERROR: test_back mismatch (expected 1)\n"); + } + + // Set front to 1 + bv.set_front(true); + if (bv.test_front() != true) + { + ::fast_io::io::panic("ERROR: set_front failed (expected 1)\n"); + } + + // Reset front to 0 + bv.reset_front(); + if (bv.test_front() != false) + { + ::fast_io::io::panic("ERROR: reset_front failed (expected 0)\n"); + } + + // Set back to 0 + bv.set_back(false); + if (bv.test_back() != false) + { + ::fast_io::io::panic("ERROR: set_back failed (expected 0)\n"); + } + + // Reset back to 0 again (should remain 0) + bv.reset_back(); + if (bv.test_back() != false) + { + ::fast_io::io::panic("ERROR: reset_back failed (expected 0)\n"); + } + + // Now set front/back using unchecked versions + bv.set_front_unchecked(true); + if (bv.test_front() != true) + { + ::fast_io::io::panic("ERROR: set_front_unchecked failed\n"); + } + + bv.set_back_unchecked(true); + if (bv.test_back() != true) + { + ::fast_io::io::panic("ERROR: set_back_unchecked failed\n"); + } + + // Reset both using unchecked versions + bv.reset_front_unchecked(); + if (bv.test_front() != false) + { + ::fast_io::io::panic("ERROR: reset_front_unchecked failed\n"); + } + + bv.reset_back_unchecked(); + if (bv.test_back() != false) + { + ::fast_io::io::panic("ERROR: reset_back_unchecked failed\n"); + } + + ::fast_io::io::print("bitvec front/back test finished\n"); +} + +inline void test_bitvec_pop_back() +{ + ::fast_io::io::perr("=== bitvec pop_back test ===\n"); + + ::fast_io::bitvec bv; + + for (::std::size_t i{}; i != 128u; ++i) + { + bv.push_back((i & 1u) != 0u); + } + + for (::std::size_t i{}; i != 128u; ++i) + { + bool expected = ((128u - 1u - i) & 1u) != 0u; + bool popped = bv.pop_back(); + if (popped != expected) + { + ::fast_io::io::panicln("ERROR: pop_back mismatch at iteration ", i); + } + } + + if (bv.size() != 0u) + { + ::fast_io::io::panic("ERROR: size not zero after popping all bits\n"); + } + + ::fast_io::io::print("bitvec pop_back test finished\n"); +} + +inline void test_bitvec_copy_constructor() +{ + ::fast_io::io::perr("=== bitvec copy constructor test ===\n"); + + ::fast_io::bitvec src; + + for (::std::size_t i{}; i != 2048u; ++i) + { + src.push_back((i & 3u) == 1u); + } + + ::fast_io::bitvec dst(src); + + if (dst.size() != src.size()) + { + ::fast_io::io::panic("ERROR: size mismatch in copy constructor\n"); + } + + for (::std::size_t i{}; i != src.size(); ++i) + { + if (dst.test(i) != src.test(i)) + { + ::fast_io::io::panicln("ERROR: copy constructor mismatch at index ", i); + } + } + + // Modify src to ensure deep copy + for (::std::size_t i{}; i != src.size(); ++i) + { + src.set_unchecked(i, !src.test(i)); + } + + // dst must remain unchanged + for (::std::size_t i{}; i != dst.size(); ++i) + { + bool expected = ((i & 3u) == 1u); + if (dst.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: deep copy violated at index ", i); + } + } + + ::fast_io::io::print("bitvec copy constructor test finished\n"); +} + +inline void test_bitvec_copy_assignment() +{ + ::fast_io::io::perr("=== bitvec copy assignment test ===\n"); + + ::fast_io::bitvec src; + ::fast_io::bitvec dst; + + // Use a valid mask: (i & 7u) can equal 2u + for (::std::size_t i{}; i != 3000u; ++i) + { + src.push_back((i & 7u) == 2u); + } + + // Fill dst with garbage first + for (::std::size_t i{}; i != 777u; ++i) + { + dst.push_back(true); + } + + dst = src; + + if (dst.size() != src.size()) + { + ::fast_io::io::panic("ERROR: size mismatch in copy assignment\n"); + } + + for (::std::size_t i{}; i != src.size(); ++i) + { + if (dst.test(i) != src.test(i)) + { + ::fast_io::io::panicln("ERROR: copy assignment mismatch at index ", i); + } + } + + // Modify src to ensure deep copy + for (::std::size_t i{}; i != src.size(); ++i) + { + src.set_unchecked(i, !src.test(i)); + } + + // dst must remain unchanged + for (::std::size_t i{}; i != dst.size(); ++i) + { + bool expected = ((i & 7u) == 2u); + if (dst.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: deep copy violated in assignment at index ", i); + } + } + + // Self-assignment + dst = dst; + + for (::std::size_t i{}; i != dst.size(); ++i) + { + bool expected = ((i & 7u) == 2u); + if (dst.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: self-assignment changed data at index ", i); + } + } + + ::fast_io::io::print("bitvec copy assignment test finished\n"); +} + +inline void test_bitvec_move() +{ + ::fast_io::io::perr("=== bitvec move test ===\n"); + + ::fast_io::bitvec src; + + for (::std::size_t i{}; i != 1024u; ++i) + { + src.push_back((i & 7u) == 3u); + } + + ::fast_io::bitvec moved(::std::move(src)); + + if (moved.size() != 1024u) + { + ::fast_io::io::panic("ERROR: size mismatch in move constructor\n"); + } + + for (::std::size_t i{}; i != 1024u; ++i) + { + bool expected = ((i & 7u) == 3u); + if (moved.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: move constructor mismatch at index ", i); + } + } + + // src must be empty + if (src.size() != 0u) + { + ::fast_io::io::panic("ERROR: moved-from bitvec not empty\n"); + } + + ::fast_io::io::print("bitvec move test finished\n"); +} + +inline void test_bitvec_flip() +{ + ::fast_io::io::perr("=== bitvec flip test ===\n"); + + ::fast_io::bitvec bv; + + // Push 64 bits: 0 1 0 1 0 1 ... + for (::std::size_t i{}; i != 64u; ++i) + { + bv.push_back((i & 1u) != 0u); + } + + // Flip all bits using checked flip() + for (::std::size_t i{}; i != 64u; ++i) + { + bv.flip(i); + } + + // Verify: all bits should now be inverted + for (::std::size_t i{}; i != 64u; ++i) + { + bool expected = ((i & 1u) == 0u); // original was (i&1)!=0, so flipped + if (bv.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: flip() mismatch at index ", i); + } + } + + // Flip again using unchecked version → should restore original pattern + for (::std::size_t i{}; i != 64u; ++i) + { + bv.flip_unchecked(i); + } + + for (::std::size_t i{}; i != 64u; ++i) + { + bool expected = (i & 1u) != 0u; + if (bv.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: flip_unchecked() mismatch at index ", i); + } + } + + // Test flip_front and flip_back + // Current front = 0, back = 1 + if (bv.test_front() != false) + { + ::fast_io::io::panic("ERROR: test_front mismatch before flip_front\n"); + } + if (bv.test_back() != true) + { + ::fast_io::io::panic("ERROR: test_back mismatch before flip_back\n"); + } + + bv.flip_front(); + if (bv.test_front() != true) + { + ::fast_io::io::panic("ERROR: flip_front failed\n"); + } + + bv.flip_back(); + if (bv.test_back() != false) + { + ::fast_io::io::panic("ERROR: flip_back failed\n"); + } + + // Now test unchecked versions + bv.flip_front_unchecked(); + if (bv.test_front() != false) + { + ::fast_io::io::panic("ERROR: flip_front_unchecked failed\n"); + } + + bv.flip_back_unchecked(); + if (bv.test_back() != true) + { + ::fast_io::io::panic("ERROR: flip_back_unchecked failed\n"); + } + + ::fast_io::io::print("bitvec flip test finished\n"); +} + +inline void test_bitvec_reserve_and_push_back_unchecked() +{ + ::fast_io::io::perr("=== bitvec reserve + push_back_unchecked test ===\n"); + + ::fast_io::bitvec bv; + + // Reserve space for 5000 bits + bv.reserve(5000); + + if (bv.capacity() < 5000) + { + ::fast_io::io::panic("ERROR: reserve did not increase capacity correctly\n"); + } + + // Now push bits WITHOUT triggering reallocation + // Use push_back_unchecked since we know capacity is sufficient + for (::std::size_t i{}; i != 5000u; ++i) + { + bv.push_back_unchecked((i & 1u) != 0u); + } + + if (bv.size() != 5000u) + { + ::fast_io::io::panic("ERROR: size mismatch after push_back_unchecked\n"); + } + + // Verify pattern + for (::std::size_t i{}; i != 5000u; ++i) + { + bool expected = (i & 1u) != 0u; + if (bv.test(i) != expected) + { + ::fast_io::io::panicln("ERROR: push_back_unchecked mismatch at index ", i); + } + } + + ::fast_io::io::print("bitvec reserve + push_back_unchecked test finished\n"); +} + +int main() +{ + test_bitvec_basic(); + test_bitvec_set_reset(); + test_bitvec_front_back(); + test_bitvec_pop_back(); + test_bitvec_copy_constructor(); + test_bitvec_copy_assignment(); + test_bitvec_move(); + test_bitvec_flip(); + test_bitvec_reserve_and_push_back_unchecked(); + + ::fast_io::io::print("All bitvec tests finished\n"); +}