From e78e4ea8d54d73813522cec78bab212e82b5e4df Mon Sep 17 00:00:00 2001 From: Green Sky Date: Tue, 25 Jun 2024 12:44:43 +0200 Subject: [PATCH] move in bitset and test --- CMakeLists.txt | 19 +++ solanaceae/util/bitset.hpp | 107 ++++++++++++++++ test/CMakeLists.txt | 14 +++ test/bitset_tests.cpp | 245 +++++++++++++++++++++++++++++++++++++ 4 files changed, 385 insertions(+) create mode 100644 solanaceae/util/bitset.hpp create mode 100644 test/CMakeLists.txt create mode 100644 test/bitset_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f6143b..64d9892 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,19 @@ cmake_minimum_required(VERSION 3.9 FATAL_ERROR) project(solanaceae) +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(SOLANACEAE_UTIL_STANDALONE ON) +else() + set(SOLANACEAE_UTIL_STANDALONE OFF) +endif() +message("II SOLANACEAE_UTIL_STANDALONE " ${SOLANACEAE_UTIL_STANDALONE}) + +option(SOLANACEAE_UTIL_BUILD_TESTING "Build the solanaceae_util tests" ${SOLANACEAE_UTIL_STANDALONE}) +message("II SOLANACEAE_UTIL_BUILD_TESTING " ${SOLANACEAE_UTIL_BUILD_TESTING}) + add_library(solanaceae_util ./solanaceae/util/span.hpp + ./solanaceae/util/bitset.hpp ./solanaceae/util/utils.hpp ./solanaceae/util/utils.cpp @@ -40,3 +51,11 @@ target_compile_features(solanaceae_file2 PUBLIC cxx_std_17) target_link_libraries(solanaceae_file2 PUBLIC solanaceae_util ) + +######################################## + +if (SOLANACEAE_UTIL_BUILD_TESTING) + include(CTest) + add_subdirectory(./test) +endif() + diff --git a/solanaceae/util/bitset.hpp b/solanaceae/util/bitset.hpp new file mode 100644 index 0000000..1b04727 --- /dev/null +++ b/solanaceae/util/bitset.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include + + +#include + +// runtime sized bitset (stl so sad) +// size is round up to bytes +struct BitSet { + std::vector _bytes; + + BitSet(void) = default; + BitSet(const BitSet&) = default; + BitSet(BitSet&&) = default; + BitSet(size_t size) { + _bytes.resize((size+7)/8); + } + + BitSet& operator=(const BitSet&) = default; + BitSet& operator=(BitSet&&) = default; + + bool operator[](size_t pos) const { + assert(pos < size_bits()); + if (pos >= size_bits()) { + return false; + } + + const size_t pos_in_bytes = pos/8; + + assert(pos_in_bytes < size_bytes()); + if (pos_in_bytes >= size_bytes()) { + return false; + } + + const size_t pos_rest_bits = pos%8; + + // bits are ordered high to low + return _bytes[pos_in_bytes] & ((0x1 << 7) >> pos_rest_bits); + } + + void set(size_t pos) { + assert(pos < _bytes.size()*8); + const size_t pos_in_bytes = pos/8; + assert(pos_in_bytes < _bytes.size()); + + const size_t pos_rest_bits = pos%8; + + // bits are ordered high to low + _bytes[pos_in_bytes] |= ((0x1 << 7) >> pos_rest_bits); + } + + void unset(size_t pos) { + assert(pos < _bytes.size()*8); + const size_t pos_in_bytes = pos/8; + assert(pos_in_bytes < _bytes.size()); + + const size_t pos_rest_bits = pos%8; + + // bits are ordered high to low + _bytes[pos_in_bytes] &= ~((0x1 << 7) >> pos_rest_bits); + } + + uint8_t* data(void) { + return _bytes.data(); + } + + size_t size_bits(void) const { + return _bytes.size()*8; + } + + size_t size_bytes(void) const { + return _bytes.size(); + } + + BitSet& merge(const BitSet& other) { + if (other.size_bytes() > size_bytes()) { + _bytes.resize(other.size_bytes()); + } + + for (size_t i = 0; i < size_bytes() && i < other.size_bytes(); i++) { + _bytes[i] |= other._bytes[i]; + } + + return *this; + } + + // start is the first bit in other relative to self + BitSet& merge(const BitSet& other, size_t start) { + // TODO: efficent implementation + + size_t need_size_bits = other.size_bits() + start; + if (need_size_bits > size_bits()) { + _bytes.resize((need_size_bits+7)/8); + } + + for (size_t i = 0; i < other.size_bits(); i++) { + if (other[i]) { + set(start+i); + } + } + + return *this; + } +}; + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..4868932 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.9...3.24 FATAL_ERROR) + +project(solanaceae) + +add_executable(solanaceae_util_bitset_test + ./bitset_tests.cpp +) + +target_link_libraries(solanaceae_util_bitset_test PUBLIC + solanaceae_util +) + +add_test(NAME solanaceae_util_bitset_test COMMAND solanaceae_util_bitset_test) + diff --git a/test/bitset_tests.cpp b/test/bitset_tests.cpp new file mode 100644 index 0000000..fdbd4e9 --- /dev/null +++ b/test/bitset_tests.cpp @@ -0,0 +1,245 @@ +#include + +#include +#include +#include + +int main(void) { + + // #################### + + for (size_t i = 1; i <= 8; i++) { + BitSet bs(i); + assert(bs._bytes.size() == 1); + } + + { + BitSet bs(9); + assert(bs._bytes.size() == 2); + } + + { + BitSet bs(24); + assert(bs._bytes.size() == 3); + } + + // #################### + + { // simple bit sets in 1 byte + BitSet bs(8); + assert(bs._bytes.size() == 1); + assert(bs._bytes.at(0) == 0x00); + + bs.set(0); + assert(bs._bytes.at(0) == 0b10000000); + assert(bs[0]); + assert(!bs[1]); + + bs.set(7); + assert(bs._bytes.at(0) == 0b10000001); + assert(bs[7]); + + bs.unset(0); + assert(bs._bytes.at(0) == 0b00000001); + assert(!bs[0]); + + bs.set(6); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs[6]); + + // useless unset + bs.unset(0); + assert(bs._bytes.at(0) == 0b00000011); + assert(!bs[0]); + + // useless set + bs.set(7); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs[7]); + } + + { // simple bit sets in 2 bytes + BitSet bs(16); + assert(bs._bytes.size() == 2); + assert(bs._bytes.at(0) == 0x00); + assert(bs._bytes.at(1) == 0x00); + + // first same as before, making sure no side effects happen + + bs.set(0); + assert(bs._bytes.at(0) == 0b10000000); + assert(bs._bytes.at(1) == 0b00000000); + assert(bs[0]); + + bs.set(7); + assert(bs._bytes.at(0) == 0b10000001); + assert(bs._bytes.at(1) == 0b00000000); + assert(bs[7]); + + bs.unset(0); + assert(bs._bytes.at(0) == 0b00000001); + assert(bs._bytes.at(1) == 0b00000000); + + bs.set(6); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b00000000); + + // useless unset + bs.unset(0); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b00000000); + + // useless set + bs.set(7); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b00000000); + + // now in second byte + + bs.set(8); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b10000000); + assert(bs[8]); + + bs.set(9); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b11000000); + assert(bs[9]); + + bs.set(15); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b11000001); + assert(bs[15]); + + bs.unset(9); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b10000001); + } + + // random long range set tests + for (size_t iml = 0; iml < 1000; iml++) { + std::default_random_engine rng(1337*17); + + size_t bit_pos = rng()%16384; + size_t total_bit_count = bit_pos + rng()%16384; // so max 32768 + + BitSet bs(total_bit_count); + assert(bs.size_bytes() == (total_bit_count+7)/8); + + for (size_t i = 0; i < total_bit_count; i++) { + assert(!bs[i]); + } + + bs.set(bit_pos); + assert(bs[bit_pos]); + + for (size_t i = 0; i < total_bit_count; i++) { + if (i == bit_pos) { + assert(bs[i]); + } else { + assert(!bs[i]); + } + } + } + + { // merging lets start simple + BitSet bs1(8); + BitSet bs2(8); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + + bs2.set(4); + assert(bs2._bytes.at(0) == 0b00001000); + + bs1.merge(bs2); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs1._bytes.at(0) == 0b00001000); + } + + { // merging + BitSet bs1(8); + BitSet bs2(8); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + + bs1.set(1); + bs2.set(4); + assert(bs1._bytes.at(0) == 0b01000000); + assert(bs2._bytes.at(0) == 0b00001000); + + bs1.merge(bs2); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs1._bytes.at(0) == 0b01001000); + } + + { // merging 2 bytes + BitSet bs1(16); + BitSet bs2(8); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs1._bytes.at(1) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + + bs1.set(1); + bs1.set(8); + assert(bs1._bytes.at(0) == 0b01000000); + assert(bs1._bytes.at(1) == 0b10000000); + + bs2.set(4); + assert(bs2._bytes.at(0) == 0b00001000); + + bs1.merge(bs2); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs1._bytes.at(0) == 0b01001000); + assert(bs1._bytes.at(1) == 0b10000000); + } + + { // merging larger in smaller + BitSet bs1(8); + BitSet bs2(16); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + assert(bs2._bytes.at(1) == 0b00000000); + + assert(bs1.size_bytes() != bs2.size_bytes()); + + bs1.set(1); + assert(bs1._bytes.at(0) == 0b01000000); + + bs2.set(4); + bs2.set(8); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs2._bytes.at(1) == 0b10000000); + + bs1.merge(bs2); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs2._bytes.at(1) == 0b10000000); + assert(bs1._bytes.at(0) == 0b01001000); + assert(bs1._bytes.at(1) == 0b10000000); + + assert(bs1.size_bytes() == bs2.size_bytes()); + } + + { // offset merge simple + BitSet bs1(16); + BitSet bs2(8); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs1._bytes.at(1) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + + bs1.set(1); + bs1.set(8); + assert(bs1._bytes.at(0) == 0b01000000); + assert(bs1._bytes.at(1) == 0b10000000); + + bs2.set(4); + assert(bs2._bytes.at(0) == 0b00001000); + + bs1.merge(bs2, 8); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs1._bytes.at(0) == 0b01000000); + assert(bs1._bytes.at(1) == 0b10001000); + } + + return 0; +} +