From 471a472faf58da89a7b3843fc7acabd6586e857d Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 19 Nov 2025 14:56:45 -0500 Subject: [PATCH 1/9] Set up for code generation bots --- CLAUDE.md | 1 + README_BOTS.md | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 120000 CLAUDE.md create mode 100644 README_BOTS.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..01cf8311 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +README_BOTS.md \ No newline at end of file diff --git a/README_BOTS.md b/README_BOTS.md new file mode 100644 index 00000000..064ef542 --- /dev/null +++ b/README_BOTS.md @@ -0,0 +1,25 @@ +# Useful Commands + +## Building +To build the project (for most code changes): + +``` +(mkdir -p build && cd build && cmake .. -DRUN_DOXYGEN=OFF -DBUILD_PYTHON_BINDINGS=OFF && make -j8) +``` + +If something looks weirdly wrong with the CMake configuration, you can `rm -Rf build` and try the build again. + +## Testing + +To run the tests after building: + +``` +./bin/test_libbdsg +``` + +# Layout + +All the code is in `bdsg/src` and `bdsg/include/bdsg`. Dependencies (like the HandleGraph headers) are in `bdsg/deps`. + +The `*.classfragment` files in `bdsg/include/bdsg/internal` are used to assemble "proxy" objects that wrap and expose all the methods of a contained object which for technical reasons isn't actually allowed to inherit from a base class, in order to provide something that does actually inherit from that base class. Try not to fiddle with this system. + From 32eb32d0065aed94e031e2ab9ffee534369129e7 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 19 Nov 2025 14:59:27 -0500 Subject: [PATCH 2/9] Explain testing --- README_BOTS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README_BOTS.md b/README_BOTS.md index 064ef542..b3a49320 100644 --- a/README_BOTS.md +++ b/README_BOTS.md @@ -17,6 +17,10 @@ To run the tests after building: ./bin/test_libbdsg ``` +The test code is in `bdsg/src/test_libbdsg.cpp`. It doesn't use a unit test framework, just a convention of test functions that check things with asserts. The tests should be ordered from low-level to high-level because we stop at the first failure. + +If you want to run just a single test, you need to comment out the calls to all the other test functions in the main function with `/* */` and rebuild. + # Layout All the code is in `bdsg/src` and `bdsg/include/bdsg`. Dependencies (like the HandleGraph headers) are in `bdsg/deps`. From 1502ecc2d6528042cb01c98624af9175f1fe8892 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 19 Nov 2025 17:13:09 -0500 Subject: [PATCH 3/9] Add automatically generated forward iterators and tests from Anthropic Claude --- bdsg/include/bdsg/internal/packed_structs.hpp | 457 +++++++- bdsg/src/test_libbdsg.cpp | 1026 +++++++++++++++++ 2 files changed, 1462 insertions(+), 21 deletions(-) diff --git a/bdsg/include/bdsg/internal/packed_structs.hpp b/bdsg/include/bdsg/internal/packed_structs.hpp index 539c991d..816ab128 100644 --- a/bdsg/include/bdsg/internal/packed_structs.hpp +++ b/bdsg/include/bdsg/internal/packed_structs.hpp @@ -37,9 +37,13 @@ class PackedVector { private: using IntVector = typename IntVectorFor::type; public: + + /// Forward declaration of iterator + class iterator; + /// Constructor (starts empty) PackedVector(); - + // TODO: constructor templated on allocator types conflicts with all the // other 1-argument constructor overloads. @@ -117,11 +121,59 @@ class PackedVector { /// Reports the amount of memory consumed by this object in bytes. size_t memory_usage() const; - + + /// Iterator to the first element + iterator begin() const; + + /// Iterator to the past-the-end element + iterator end() const; + /// Returns true if the contents are identical (but not necessarily storage /// parameters, such as pointer to data, capacity, bit width, etc.). inline bool operator==(const PackedVector& other) const; - + + /** + * A forward iterator for PackedVector that provides read-only access to elements + */ + class iterator { + public: + // Iterator traits for standard library compatibility + using iterator_category = std::forward_iterator_tag; + using value_type = uint64_t; + using difference_type = typename std::make_signed::type; + using pointer = void; + using reference = uint64_t; + + // Standard iterator operations + iterator(const iterator& other) = default; + iterator& operator=(const iterator& other) = default; + ~iterator() = default; + + /// Pre-increment operator + iterator& operator++(); + + /// Post-increment operator + iterator operator++(int); + + /// Dereference operator - returns value at current position + uint64_t operator*() const; + + /// Equality comparison + bool operator==(const iterator& other) const; + + /// Inequality comparison + bool operator!=(const iterator& other) const; + + private: + // Private constructor - only PackedVector can create iterators + iterator(const PackedVector* vec, size_t idx); + + const PackedVector* vec_ptr = nullptr; + size_t index = 0; + + friend class PackedVector; + }; + private: // We don't allocate ourselves, so we don't need to hold an allocator. @@ -152,23 +204,26 @@ class PagedVector { using PageHolder = typename VectorFor::template type; public: - - /// Construct (starts empty) + + /// Forward declaration of iterator + class iterator; + + /// Construct (starts empty) PagedVector(); - + /// Construct from contents in a stream PagedVector(istream& in); - + /// Move constructor PagedVector(PagedVector&& other) = default; /// Move assignment operator PagedVector& operator=(PagedVector&& other) = default; - + /// Copy constructor PagedVector(const PagedVector& other) = default; /// Copy assignment operator PagedVector& operator=(const PagedVector& other) = default; - + // Destructor ~PagedVector(); @@ -212,18 +267,66 @@ class PagedVector { /// Returns the page width of the vector inline size_t page_width() const; - + + /// Iterator to the first element + iterator begin() const; + + /// Iterator to the past-the-end element + iterator end() const; + /// Reports the amount of memory consumed by this object in bytes size_t memory_usage() const; - + + /** + * A forward iterator for PagedVector that provides read-only access to elements + */ + class iterator { + public: + // Iterator traits for standard library compatibility + using iterator_category = std::forward_iterator_tag; + using value_type = uint64_t; + using difference_type = typename std::make_signed::type; + using pointer = void; + using reference = uint64_t; + + // Standard iterator operations + iterator(const iterator& other) = default; + iterator& operator=(const iterator& other) = default; + ~iterator() = default; + + /// Pre-increment operator + iterator& operator++(); + + /// Post-increment operator + iterator operator++(int); + + /// Dereference operator - returns value at current position + uint64_t operator*() const; + + /// Equality comparison + bool operator==(const iterator& other) const; + + /// Inequality comparison + bool operator!=(const iterator& other) const; + + private: + // Private constructor - only PagedVector can create iterators + iterator(const PagedVector* vec, size_t idx); + + const PagedVector* vec_ptr = nullptr; + size_t index = 0; + + friend class PagedVector; + }; + private: - + inline uint64_t to_diff(const uint64_t& value, const uint64_t& page) const; inline uint64_t from_diff(const uint64_t& diff, const uint64_t& page) const; - + // The number of entries filled so far size_t filled = 0; - + // Evenly spaced entries from the vector Page anchors; // All entries in the vector expressed as a difference from the preceding page value @@ -244,9 +347,13 @@ class RobustPagedVector { using PackedVec = PackedVector; using PagedVec = PagedVector; public: + + /// Forward declaration of iterator + class iterator; + /// Construct (starts empty) RobustPagedVector(); - + /// Construct from contents in a stream RobustPagedVector(istream& in); @@ -303,10 +410,58 @@ class RobustPagedVector { /// Returns the page width of the vector inline size_t page_width() const; - + + /// Iterator to the first element + iterator begin() const; + + /// Iterator to the past-the-end element + iterator end() const; + /// Reports the amount of memory consumed by this object in bytes size_t memory_usage() const; - + + /** + * A forward iterator for RobustPagedVector that provides read-only access to elements + */ + class iterator { + public: + // Iterator traits for standard library compatibility + using iterator_category = std::forward_iterator_tag; + using value_type = uint64_t; + using difference_type = typename std::make_signed::type; + using pointer = void; + using reference = uint64_t; + + // Standard iterator operations + iterator(const iterator& other) = default; + iterator& operator=(const iterator& other) = default; + ~iterator() = default; + + /// Pre-increment operator + iterator& operator++(); + + /// Post-increment operator + iterator operator++(int); + + /// Dereference operator - returns value at current position + uint64_t operator*() const; + + /// Equality comparison + bool operator==(const iterator& other) const; + + /// Inequality comparison + bool operator!=(const iterator& other) const; + + private: + // Private constructor - only RobustPagedVector can create iterators + iterator(const RobustPagedVector* vec, size_t idx); + + const RobustPagedVector* vec_ptr = nullptr; + size_t index = 0; + + friend class RobustPagedVector; + }; + private: /// The first page_size entries go in this vector @@ -328,6 +483,10 @@ class PackedDeque { private: using PackedVec = PackedVector; public: + + /// Forward declaration of iterator + class iterator; + /// Construct empty PackedDeque(void); /// Construct from contents in a stream @@ -382,10 +541,58 @@ class PackedDeque { /// Empty the contents inline void clear(); - + + /// Iterator to the first element + iterator begin() const; + + /// Iterator to the past-the-end element + iterator end() const; + /// Reports the amount of memory consumed by this object in bytes. size_t memory_usage() const; - + + /** + * A forward iterator for PackedDeque that provides read-only access to elements + */ + class iterator { + public: + // Iterator traits for standard library compatibility + using iterator_category = std::forward_iterator_tag; + using value_type = uint64_t; + using difference_type = typename std::make_signed::type; + using pointer = void; + using reference = uint64_t; + + // Standard iterator operations + iterator(const iterator& other) = default; + iterator& operator=(const iterator& other) = default; + ~iterator() = default; + + /// Pre-increment operator + iterator& operator++(); + + /// Post-increment operator + iterator operator++(int); + + /// Dereference operator - returns value at current position + uint64_t operator*() const; + + /// Equality comparison + bool operator==(const iterator& other) const; + + /// Inequality comparison + bool operator!=(const iterator& other) const; + + private: + // Private constructor - only PackedDeque can create iterators + iterator(const PackedDeque* deque, size_t idx); + + const PackedDeque* deque_ptr = nullptr; + size_t index = 0; + + friend class PackedDeque; + }; + private: inline void contract(); @@ -713,6 +920,58 @@ size_t PackedVector::memory_usage() const { return sizeof(filled) + sizeof(vec) + vec.capacity() / 8; } +///////////////////// +/// PackedVector::iterator +///////////////////// + +template +PackedVector::iterator::iterator(const PackedVector* vec, size_t idx) + : vec_ptr(vec), index(idx) { + // Constructor +} + +template +typename PackedVector::iterator& +PackedVector::iterator::operator++() { + ++index; + return *this; +} + +template +typename PackedVector::iterator +PackedVector::iterator::operator++(int) { + iterator tmp = *this; + ++index; + return tmp; +} + +template +uint64_t PackedVector::iterator::operator*() const { + return vec_ptr->get(index); +} + +template +bool PackedVector::iterator::operator==(const iterator& other) const { + return vec_ptr == other.vec_ptr && index == other.index; +} + +template +bool PackedVector::iterator::operator!=(const iterator& other) const { + return !(*this == other); +} + +template +typename PackedVector::iterator +PackedVector::begin() const { + return iterator(this, 0); +} + +template +typename PackedVector::iterator +PackedVector::end() const { + return iterator(this, filled); +} + ///////////////////// /// PackedDeque ///////////////////// @@ -872,7 +1131,59 @@ inline void PackedDeque::clear() { filled = 0; begin_idx = 0; } - + +///////////////////// +/// PackedDeque::iterator +///////////////////// + +template +PackedDeque::iterator::iterator(const PackedDeque* deque, size_t idx) + : deque_ptr(deque), index(idx) { + // Constructor +} + +template +typename PackedDeque::iterator& +PackedDeque::iterator::operator++() { + ++index; + return *this; +} + +template +typename PackedDeque::iterator +PackedDeque::iterator::operator++(int) { + iterator tmp = *this; + ++index; + return tmp; +} + +template +uint64_t PackedDeque::iterator::operator*() const { + return deque_ptr->get(index); +} + +template +bool PackedDeque::iterator::operator==(const iterator& other) const { + return deque_ptr == other.deque_ptr && index == other.index; +} + +template +bool PackedDeque::iterator::operator!=(const iterator& other) const { + return !(*this == other); +} + +template +typename PackedDeque::iterator +PackedDeque::begin() const { + return iterator(this, 0); +} + +template +typename PackedDeque::iterator +PackedDeque::end() const { + return iterator(this, filled); +} + ///////////////////// /// PagedVector ///////////////////// @@ -1064,7 +1375,7 @@ inline uint64_t PagedVector::to_diff(const uint64_t& value, template inline uint64_t PagedVector::from_diff(const uint64_t& diff, const uint64_t& anchor) const { // convert backward from the transformation described in to_diff - + if (diff == 0) { return 0; } @@ -1076,6 +1387,58 @@ inline uint64_t PagedVector::from_diff(const uint64_t& diff, } } +///////////////////// +/// PagedVector::iterator +///////////////////// + +template +PagedVector::iterator::iterator(const PagedVector* vec, size_t idx) + : vec_ptr(vec), index(idx) { + // Constructor +} + +template +typename PagedVector::iterator& +PagedVector::iterator::operator++() { + ++index; + return *this; +} + +template +typename PagedVector::iterator +PagedVector::iterator::operator++(int) { + iterator tmp = *this; + ++index; + return tmp; +} + +template +uint64_t PagedVector::iterator::operator*() const { + return vec_ptr->get(index); +} + +template +bool PagedVector::iterator::operator==(const iterator& other) const { + return vec_ptr == other.vec_ptr && index == other.index; +} + +template +bool PagedVector::iterator::operator!=(const iterator& other) const { + return !(*this == other); +} + +template +typename PagedVector::iterator +PagedVector::begin() const { + return iterator(this, 0); +} + +template +typename PagedVector::iterator +PagedVector::end() const { + return iterator(this, filled); +} + ///////////////////// /// RobustPagedVector ///////////////////// @@ -1202,6 +1565,58 @@ inline size_t RobustPagedVector::page_width() const { return latter_pages.page_width(); } +///////////////////// +/// RobustPagedVector::iterator +///////////////////// + +template +RobustPagedVector::iterator::iterator(const RobustPagedVector* vec, size_t idx) + : vec_ptr(vec), index(idx) { + // Constructor +} + +template +typename RobustPagedVector::iterator& +RobustPagedVector::iterator::operator++() { + ++index; + return *this; +} + +template +typename RobustPagedVector::iterator +RobustPagedVector::iterator::operator++(int) { + iterator tmp = *this; + ++index; + return tmp; +} + +template +uint64_t RobustPagedVector::iterator::operator*() const { + return vec_ptr->get(index); +} + +template +bool RobustPagedVector::iterator::operator==(const iterator& other) const { + return vec_ptr == other.vec_ptr && index == other.index; +} + +template +bool RobustPagedVector::iterator::operator!=(const iterator& other) const { + return !(*this == other); +} + +template +typename RobustPagedVector::iterator +RobustPagedVector::begin() const { + return iterator(this, 0); +} + +template +typename RobustPagedVector::iterator +RobustPagedVector::end() const { + return iterator(this, size()); +} + ///////////////////// /// PackedSet ///////////////////// diff --git a/bdsg/src/test_libbdsg.cpp b/bdsg/src/test_libbdsg.cpp index 0d94c9e3..6b60438f 100644 --- a/bdsg/src/test_libbdsg.cpp +++ b/bdsg/src/test_libbdsg.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include // BINDER_IGNORE because Binder can't find this @@ -2874,6 +2875,248 @@ void test_packed_vector() { cerr << "PackedVector (" << typeid(PackedVectorImpl).name() << ") tests successful!" << endl; } +template +void test_packed_vector_iterators() { + // Test 1: Empty vector iteration + { + PackedVectorImpl vec; + assert(vec.begin() == vec.end()); + + size_t count = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + count++; + } + assert(count == 0); + } + + // Test 2: Single element + { + PackedVectorImpl vec; + vec.append(42); + + assert(vec.begin() != vec.end()); + + auto it = vec.begin(); + assert(*it == 42); + ++it; + assert(it == vec.end()); + } + + // Test 3: Multiple elements - basic iteration + { + PackedVectorImpl vec; + vector expected = {10, 20, 30, 40, 50}; + + for (auto val : expected) { + vec.append(val); + } + + // Iterate and compare + size_t idx = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + assert(idx < expected.size()); + assert(*it == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 4: Range-based for loop + { + PackedVectorImpl vec; + vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; + + for (auto val : expected) { + vec.append(val); + } + + size_t idx = 0; + for (auto val : vec) { + assert(idx < expected.size()); + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 5: Iterator equality and inequality + { + PackedVectorImpl vec; + vec.append(1); + vec.append(2); + vec.append(3); + + auto it1 = vec.begin(); + auto it2 = vec.begin(); + assert(it1 == it2); + + ++it2; + assert(it1 != it2); + + ++it1; + assert(it1 == it2); + } + + // Test 6: std::distance compatibility + { + PackedVectorImpl vec; + for (size_t i = 0; i < 15; i++) { + vec.append(i); + } + + auto dist = std::distance(vec.begin(), vec.end()); + assert((size_t)dist == vec.size()); + assert((size_t)dist == 15); + } + + // Test 7: std::find compatibility + { + PackedVectorImpl vec; + vec.append(10); + vec.append(20); + vec.append(30); + vec.append(40); + vec.append(50); + + auto it = std::find(vec.begin(), vec.end(), 30); + assert(it != vec.end()); + assert(*it == 30); + + auto it2 = std::find(vec.begin(), vec.end(), 999); + assert(it2 == vec.end()); + } + + // Test 8: Const iterator + { + PackedVectorImpl vec; + vec.append(5); + vec.append(15); + vec.append(25); + + const PackedVectorImpl& const_vec = vec; + + size_t count = 0; + for (auto it = const_vec.begin(); it != const_vec.end(); ++it) { + count++; + } + assert(count == 3); + + auto it = const_vec.begin(); + assert(*it == 5); + ++it; + assert(*it == 15); + ++it; + assert(*it == 25); + } + + // Test 9: Large vector with various patterns + { + PackedVectorImpl vec; + random_device rd; + default_random_engine prng(rd()); + uniform_int_distribution val_distr(0, 10000); + + vector expected; + size_t num_elements = 200; + + for (size_t i = 0; i < num_elements; i++) { + uint64_t val = val_distr(prng); + expected.push_back(val); + vec.append(val); + } + + size_t idx = 0; + for (auto val : vec) { + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 10: Iteration after modification + { + PackedVectorImpl vec; + vec.append(1); + vec.append(2); + vec.append(3); + + // First iteration + size_t count = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + count++; + } + assert(count == 3); + + // Modify + vec.append(4); + vec.set(0, 100); + + // Second iteration + vector expected = {100, 2, 3, 4}; + size_t idx = 0; + for (auto val : vec) { + assert(val == expected[idx]); + idx++; + } + assert(idx == 4); + } + + // Test 11: Bit-width changes during iteration + { + PackedVectorImpl vec; + // Start with small values (low bit-width) + vec.append(1); + vec.append(2); + vec.append(3); + + // Add large value (increases bit-width) + vec.append(1000000); + + vector expected = {1, 2, 3, 1000000}; + size_t idx = 0; + for (auto val : vec) { + assert(val == expected[idx]); + idx++; + } + assert(idx == 4); + } + + // Test 12: Iterator copy construction + { + PackedVectorImpl vec; + vec.append(10); + vec.append(20); + + auto it1 = vec.begin(); + auto it2(it1); // Copy constructor + + assert(it1 == it2); + assert(*it1 == *it2); + assert(*it1 == 10); + } + + // Test 13: Iterator assignment + { + PackedVectorImpl vec; + vec.append(10); + vec.append(20); + vec.append(30); + + auto it1 = vec.begin(); + auto it2 = vec.begin(); + ++it2; + + assert(*it1 == 10); + assert(*it2 == 20); + + it1 = it2; // Assignment + assert(it1 == it2); + assert(*it1 == 20); + } + + cerr << "PackedVector (" << typeid(PackedVectorImpl).name() << ") iterator tests successful!" << endl; +} + template void test_paged_vector() { enum vec_op_t {SET = 0, GET = 1, APPEND = 2, POP = 3, SERIALIZE = 4}; @@ -2968,6 +3211,499 @@ void test_paged_vector() { cerr << "PagedVector (" << typeid(PagedVectorImpl).name() << ") tests successful!" << endl; } +template +void test_paged_vector_iterators() { + // Test 1: Empty vector iteration + { + PagedVectorImpl vec; + assert(vec.begin() == vec.end()); + + size_t count = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + count++; + } + assert(count == 0); + } + + // Test 2: Single element + { + PagedVectorImpl vec; + vec.append(42); + + assert(vec.begin() != vec.end()); + + auto it = vec.begin(); + assert(*it == 42); + ++it; + assert(it == vec.end()); + } + + // Test 3: Multiple elements - basic iteration + { + PagedVectorImpl vec; + vector expected = {10, 20, 30, 40, 50}; + + for (auto val : expected) { + vec.append(val); + } + + // Iterate and compare + size_t idx = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + assert(idx < expected.size()); + assert(*it == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 4: Range-based for loop + { + PagedVectorImpl vec; + vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; + + for (auto val : expected) { + vec.append(val); + } + + size_t idx = 0; + for (auto val : vec) { + assert(idx < expected.size()); + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 5: Iterator equality and inequality + { + PagedVectorImpl vec; + vec.append(1); + vec.append(2); + vec.append(3); + + auto it1 = vec.begin(); + auto it2 = vec.begin(); + assert(it1 == it2); + + ++it2; + assert(it1 != it2); + + ++it1; + assert(it1 == it2); + } + + // Test 6: Iteration over page boundaries (important for PagedVector) + { + PagedVectorImpl vec; + // Create a temp vector to get page_width + size_t num_elements = vec.page_width() * 3 + 5; // Cross multiple pages + + for (size_t i = 0; i < num_elements; i++) { + vec.append(i * 7); // Some pattern + } + + size_t idx = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + assert(*it == idx * 7); + idx++; + } + assert(idx == num_elements); + } + + // Test 7: std::distance compatibility + { + PagedVectorImpl vec; + for (size_t i = 0; i < 15; i++) { + vec.append(i); + } + + auto dist = std::distance(vec.begin(), vec.end()); + assert((size_t)dist == vec.size()); + assert((size_t)dist == 15); + } + + // Test 8: std::find compatibility + { + PagedVectorImpl vec; + vec.append(10); + vec.append(20); + vec.append(30); + vec.append(40); + vec.append(50); + + auto it = std::find(vec.begin(), vec.end(), 30); + assert(it != vec.end()); + assert(*it == 30); + + auto it2 = std::find(vec.begin(), vec.end(), 999); + assert(it2 == vec.end()); + } + + // Test 9: Const iterator + { + PagedVectorImpl vec; + vec.append(5); + vec.append(15); + vec.append(25); + + const PagedVectorImpl& const_vec = vec; + + size_t count = 0; + for (auto it = const_vec.begin(); it != const_vec.end(); ++it) { + count++; + } + assert(count == 3); + + auto it = const_vec.begin(); + assert(*it == 5); + ++it; + assert(*it == 15); + ++it; + assert(*it == 25); + } + + // Test 10: Large vector with various patterns + { + PagedVectorImpl vec; + random_device rd; + default_random_engine prng(rd()); + uniform_int_distribution val_distr(0, 10000); + + vector expected; + size_t num_elements = 200; + + for (size_t i = 0; i < num_elements; i++) { + uint64_t val = val_distr(prng); + expected.push_back(val); + vec.append(val); + } + + size_t idx = 0; + for (auto val : vec) { + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 11: Iteration after modification + { + PagedVectorImpl vec; + vec.append(1); + vec.append(2); + vec.append(3); + + // First iteration + size_t count = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + count++; + } + assert(count == 3); + + // Modify + vec.append(4); + vec.set(0, 100); + + // Second iteration + vector expected = {100, 2, 3, 4}; + size_t idx = 0; + for (auto val : vec) { + assert(val == expected[idx]); + idx++; + } + assert(idx == 4); + } + + // Test 12: All zeros + { + PagedVectorImpl vec; + vec.append(0); + vec.append(0); + vec.append(0); + + for (auto val : vec) { + assert(val == 0); + } + } + + // Test 13: Iterator copy construction + { + PagedVectorImpl vec; + vec.append(10); + vec.append(20); + + auto it1 = vec.begin(); + auto it2(it1); // Copy constructor + + assert(it1 == it2); + assert(*it1 == *it2); + assert(*it1 == 10); + } + + // Test 14: Iterator assignment + { + PagedVectorImpl vec; + vec.append(10); + vec.append(20); + vec.append(30); + + auto it1 = vec.begin(); + auto it2 = vec.begin(); + ++it2; + + assert(*it1 == 10); + assert(*it2 == 20); + + it1 = it2; // Assignment + assert(it1 == it2); + assert(*it1 == 20); + } + + cerr << "PagedVector (" << typeid(PagedVectorImpl).name() << ") iterator tests successful!" << endl; +} + +template +void test_robust_paged_vector_iterators() { + // Test 1: Empty vector iteration + { + RobustPagedVectorImpl vec; + assert(vec.begin() == vec.end()); + + size_t count = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + count++; + } + assert(count == 0); + } + + // Test 2: Single element + { + RobustPagedVectorImpl vec; + vec.append(42); + + assert(vec.begin() != vec.end()); + + auto it = vec.begin(); + assert(*it == 42); + ++it; + assert(it == vec.end()); + } + + // Test 3: Multiple elements - basic iteration + { + RobustPagedVectorImpl vec; + vector expected = {10, 20, 30, 40, 50}; + + for (auto val : expected) { + vec.append(val); + } + + // Iterate and compare + size_t idx = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + assert(idx < expected.size()); + assert(*it == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 4: Range-based for loop + { + RobustPagedVectorImpl vec; + vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; + + for (auto val : expected) { + vec.append(val); + } + + size_t idx = 0; + for (auto val : vec) { + assert(idx < expected.size()); + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 5: Iterator equality and inequality + { + RobustPagedVectorImpl vec; + vec.append(1); + vec.append(2); + vec.append(3); + + auto it1 = vec.begin(); + auto it2 = vec.begin(); + assert(it1 == it2); + + ++it2; + assert(it1 != it2); + + ++it1; + assert(it1 == it2); + } + + // Test 6: Iteration across first_page/latter_pages boundary + { + RobustPagedVectorImpl vec; + // Get page width and add elements before, at, and after the boundary + size_t page_width = vec.page_width(); + + // Add elements spanning the boundary + for (size_t i = 0; i < page_width + 10; i++) { + vec.append(i * 3); + } + + size_t idx = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + assert(*it == idx * 3); + idx++; + } + assert(idx == page_width + 10); + } + + // Test 7: std::distance compatibility + { + RobustPagedVectorImpl vec; + for (size_t i = 0; i < 15; i++) { + vec.append(i); + } + + auto dist = std::distance(vec.begin(), vec.end()); + assert((size_t)dist == vec.size()); + assert((size_t)dist == 15); + } + + // Test 8: std::find compatibility + { + RobustPagedVectorImpl vec; + vec.append(10); + vec.append(20); + vec.append(30); + vec.append(40); + vec.append(50); + + auto it = std::find(vec.begin(), vec.end(), 30); + assert(it != vec.end()); + assert(*it == 30); + + auto it2 = std::find(vec.begin(), vec.end(), 999); + assert(it2 == vec.end()); + } + + // Test 9: Const iterator + { + RobustPagedVectorImpl vec; + vec.append(5); + vec.append(15); + vec.append(25); + + const RobustPagedVectorImpl& const_vec = vec; + + size_t count = 0; + for (auto it = const_vec.begin(); it != const_vec.end(); ++it) { + count++; + } + assert(count == 3); + + auto it = const_vec.begin(); + assert(*it == 5); + ++it; + assert(*it == 15); + ++it; + assert(*it == 25); + } + + // Test 10: Large vector spanning multiple pages + { + RobustPagedVectorImpl vec; + random_device rd; + default_random_engine prng(rd()); + uniform_int_distribution val_distr(0, 10000); + + vector expected; + size_t num_elements = 200; + + for (size_t i = 0; i < num_elements; i++) { + uint64_t val = val_distr(prng); + expected.push_back(val); + vec.append(val); + } + + size_t idx = 0; + for (auto val : vec) { + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 11: Iteration after modification + { + RobustPagedVectorImpl vec; + vec.append(1); + vec.append(2); + vec.append(3); + + // First iteration + size_t count = 0; + for (auto it = vec.begin(); it != vec.end(); ++it) { + count++; + } + assert(count == 3); + + // Modify + vec.append(4); + vec.set(0, 100); + + // Second iteration + vector expected = {100, 2, 3, 4}; + size_t idx = 0; + for (auto val : vec) { + assert(val == expected[idx]); + idx++; + } + assert(idx == 4); + } + + // Test 12: Iterator copy construction + { + RobustPagedVectorImpl vec; + vec.append(10); + vec.append(20); + + auto it1 = vec.begin(); + auto it2(it1); // Copy constructor + + assert(it1 == it2); + assert(*it1 == *it2); + assert(*it1 == 10); + } + + // Test 13: Iterator assignment + { + RobustPagedVectorImpl vec; + vec.append(10); + vec.append(20); + vec.append(30); + + auto it1 = vec.begin(); + auto it2 = vec.begin(); + ++it2; + + assert(*it1 == 10); + assert(*it2 == 20); + + it1 = it2; // Assignment + assert(it1 == it2); + assert(*it1 == 20); + } + + cerr << "RobustPagedVector (" << typeid(RobustPagedVectorImpl).name() << ") iterator tests successful!" << endl; +} + void test_packed_deque() { enum deque_op_t {SET = 0, GET = 1, APPEND_LEFT = 2, POP_LEFT = 3, APPEND_RIGHT = 4, POP_RIGHT = 5, SERIALIZE = 6}; std::random_device rd; @@ -3075,6 +3811,276 @@ void test_packed_deque() { cerr << "PackedDeque tests successful!" << endl; } +template +void test_packed_deque_iterators() { + // Test 1: Empty deque iteration + { + PackedDequeImpl deque; + assert(deque.begin() == deque.end()); + + size_t count = 0; + for (auto it = deque.begin(); it != deque.end(); ++it) { + count++; + } + assert(count == 0); + } + + // Test 2: Single element + { + PackedDequeImpl deque; + deque.append_back(42); + + assert(deque.begin() != deque.end()); + + auto it = deque.begin(); + assert(*it == 42); + ++it; + assert(it == deque.end()); + } + + // Test 3: Multiple elements - basic iteration + { + PackedDequeImpl deque; + vector expected = {10, 20, 30, 40, 50}; + + for (auto val : expected) { + deque.append_back(val); + } + + // Iterate and compare + size_t idx = 0; + for (auto it = deque.begin(); it != deque.end(); ++it) { + assert(idx < expected.size()); + assert(*it == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 4: Range-based for loop + { + PackedDequeImpl deque; + vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; + + for (auto val : expected) { + deque.append_back(val); + } + + size_t idx = 0; + for (auto val : deque) { + assert(idx < expected.size()); + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 5: Iterator equality and inequality + { + PackedDequeImpl deque; + deque.append_back(1); + deque.append_back(2); + deque.append_back(3); + + auto it1 = deque.begin(); + auto it2 = deque.begin(); + assert(it1 == it2); + + ++it2; + assert(it1 != it2); + + ++it1; + assert(it1 == it2); + } + + // Test 6: Iteration after mixed front/back operations + { + PackedDequeImpl deque; + deque.append_back(3); + deque.append_back(4); + deque.append_front(2); + deque.append_front(1); + deque.append_back(5); + + vector expected = {1, 2, 3, 4, 5}; + size_t idx = 0; + for (auto val : deque) { + assert(val == expected[idx]); + idx++; + } + assert(idx == 5); + } + + // Test 7: Iteration with circular buffer wrap-around + { + PackedDequeImpl deque; + // Add elements to force circular buffer behavior + for (size_t i = 0; i < 20; i++) { + deque.append_back(i); + } + // Remove from front + for (size_t i = 0; i < 10; i++) { + deque.pop_front(); + } + // Add more to back + for (size_t i = 20; i < 30; i++) { + deque.append_back(i); + } + + // Should now have 10-29 + vector expected; + for (size_t i = 10; i < 30; i++) { + expected.push_back(i); + } + + size_t idx = 0; + for (auto val : deque) { + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 8: std::distance compatibility + { + PackedDequeImpl deque; + for (size_t i = 0; i < 15; i++) { + deque.append_back(i); + } + + auto dist = std::distance(deque.begin(), deque.end()); + assert((size_t)dist == deque.size()); + assert((size_t)dist == 15); + } + + // Test 9: std::find compatibility + { + PackedDequeImpl deque; + deque.append_back(10); + deque.append_back(20); + deque.append_back(30); + deque.append_back(40); + deque.append_back(50); + + auto it = std::find(deque.begin(), deque.end(), 30); + assert(it != deque.end()); + assert(*it == 30); + + auto it2 = std::find(deque.begin(), deque.end(), 999); + assert(it2 == deque.end()); + } + + // Test 10: Const iterator + { + PackedDequeImpl deque; + deque.append_back(5); + deque.append_back(15); + deque.append_back(25); + + const PackedDequeImpl& const_deque = deque; + + size_t count = 0; + for (auto it = const_deque.begin(); it != const_deque.end(); ++it) { + count++; + } + assert(count == 3); + + auto it = const_deque.begin(); + assert(*it == 5); + ++it; + assert(*it == 15); + ++it; + assert(*it == 25); + } + + // Test 11: Large deque + { + PackedDequeImpl deque; + random_device rd; + default_random_engine prng(rd()); + uniform_int_distribution val_distr(0, 10000); + + vector expected; + size_t num_elements = 200; + + for (size_t i = 0; i < num_elements; i++) { + uint64_t val = val_distr(prng); + expected.push_back(val); + deque.append_back(val); + } + + size_t idx = 0; + for (auto val : deque) { + assert(val == expected[idx]); + idx++; + } + assert(idx == expected.size()); + } + + // Test 12: Iteration after modification + { + PackedDequeImpl deque; + deque.append_back(1); + deque.append_back(2); + deque.append_back(3); + + // First iteration + size_t count = 0; + for (auto it = deque.begin(); it != deque.end(); ++it) { + count++; + } + assert(count == 3); + + // Modify + deque.append_back(4); + deque.set(0, 100); + + // Second iteration + vector expected = {100, 2, 3, 4}; + size_t idx = 0; + for (auto val : deque) { + assert(val == expected[idx]); + idx++; + } + assert(idx == 4); + } + + // Test 13: Iterator copy construction + { + PackedDequeImpl deque; + deque.append_back(10); + deque.append_back(20); + + auto it1 = deque.begin(); + auto it2(it1); // Copy constructor + + assert(it1 == it2); + assert(*it1 == *it2); + assert(*it1 == 10); + } + + // Test 14: Iterator assignment + { + PackedDequeImpl deque; + deque.append_back(10); + deque.append_back(20); + deque.append_back(30); + + auto it1 = deque.begin(); + auto it2 = deque.begin(); + ++it2; + + assert(*it1 == 10); + assert(*it2 == 20); + + it1 = it2; // Assignment + assert(it1 == it2); + assert(*it1 == 20); + } + + cerr << "PackedDeque (" << typeid(PackedDequeImpl).name() << ") iterator tests successful!" << endl; +} + void test_packed_set() { enum set_op_t {INSERT = 0, REMOVE = 1, FIND = 2}; @@ -4700,6 +5706,9 @@ int main(void) { test_packed_vector>(); test_packed_vector>(); test_packed_vector>(); + test_packed_vector_iterators>(); + test_packed_vector_iterators>(); + test_packed_vector_iterators>(); test_paged_vector>(); test_paged_vector>(); test_paged_vector>(); @@ -4707,7 +5716,24 @@ int main(void) { test_paged_vector>(); test_paged_vector>(); test_paged_vector>(); + test_paged_vector_iterators>(); + test_paged_vector_iterators>(); + test_paged_vector_iterators>(); + test_paged_vector_iterators>(); + test_paged_vector_iterators>(); + test_paged_vector_iterators>(); + test_paged_vector_iterators>(); + test_paged_vector_iterators>(); + test_robust_paged_vector_iterators>(); + test_robust_paged_vector_iterators>(); + test_robust_paged_vector_iterators>(); + test_robust_paged_vector_iterators>(); + test_robust_paged_vector_iterators>(); + test_robust_paged_vector_iterators>(); test_packed_deque(); + test_packed_deque_iterators>(); + test_packed_deque_iterators>(); + test_packed_deque_iterators>(); test_packed_set(); test_mutable_path_handle_graphs(); test_deletable_handle_graphs(); From 7ce1d0b970e02f9130c951381a4bcec20e4309d9 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 19 Nov 2025 17:20:45 -0500 Subject: [PATCH 4/9] Let Anthropic Claude consolidate tests until going on strike for more subscription fees --- bdsg/src/test_libbdsg.cpp | 380 ++++++++++---------------------------- 1 file changed, 101 insertions(+), 279 deletions(-) diff --git a/bdsg/src/test_libbdsg.cpp b/bdsg/src/test_libbdsg.cpp index 6b60438f..13b1b83d 100644 --- a/bdsg/src/test_libbdsg.cpp +++ b/bdsg/src/test_libbdsg.cpp @@ -2875,11 +2875,53 @@ void test_packed_vector() { cerr << "PackedVector (" << typeid(PackedVectorImpl).name() << ") tests successful!" << endl; } -template -void test_packed_vector_iterators() { - // Test 1: Empty vector iteration +// SFINAE helpers to detect append() vs append_back() +template +class has_append_method { + template + static auto test(int) -> decltype(std::declval().append(0), std::true_type()); + + template + static std::false_type test(...); + +public: + static constexpr bool value = decltype(test(0))::value; +}; + +template +class has_append_back_method { + template + static auto test(int) -> decltype(std::declval().append_back(0), std::true_type()); + + template + static std::false_type test(...); + +public: + static constexpr bool value = decltype(test(0))::value; +}; + +// Generic helper to append to a container (works with both append() and append_back()) +template +typename std::enable_if::value>::type +container_append(Container& c, uint64_t val) { + c.append(val); +} + +template +typename std::enable_if::value && !has_append_method::value>::type +container_append(Container& c, uint64_t val) { + c.append_back(val); +} + +/** + * Generic iterator test function that works with any vector-like container + * (PackedVector, PagedVector, RobustPagedVector, PackedDeque) + */ +template +void test_vector_like_iterators_common() { + // Test 1: Empty iteration { - PackedVectorImpl vec; + VectorLike vec; assert(vec.begin() == vec.end()); size_t count = 0; @@ -2891,8 +2933,8 @@ void test_packed_vector_iterators() { // Test 2: Single element { - PackedVectorImpl vec; - vec.append(42); + VectorLike vec; + container_append(vec, 42); assert(vec.begin() != vec.end()); @@ -2904,11 +2946,11 @@ void test_packed_vector_iterators() { // Test 3: Multiple elements - basic iteration { - PackedVectorImpl vec; + VectorLike vec; vector expected = {10, 20, 30, 40, 50}; for (auto val : expected) { - vec.append(val); + container_append(vec, val); } // Iterate and compare @@ -2923,11 +2965,11 @@ void test_packed_vector_iterators() { // Test 4: Range-based for loop { - PackedVectorImpl vec; + VectorLike vec; vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; for (auto val : expected) { - vec.append(val); + container_append(vec, val); } size_t idx = 0; @@ -2941,10 +2983,10 @@ void test_packed_vector_iterators() { // Test 5: Iterator equality and inequality { - PackedVectorImpl vec; - vec.append(1); - vec.append(2); - vec.append(3); + VectorLike vec; + container_append(vec, 1); + container_append(vec, 2); + container_append(vec, 3); auto it1 = vec.begin(); auto it2 = vec.begin(); @@ -2959,9 +3001,9 @@ void test_packed_vector_iterators() { // Test 6: std::distance compatibility { - PackedVectorImpl vec; + VectorLike vec; for (size_t i = 0; i < 15; i++) { - vec.append(i); + container_append(vec, i); } auto dist = std::distance(vec.begin(), vec.end()); @@ -2971,12 +3013,12 @@ void test_packed_vector_iterators() { // Test 7: std::find compatibility { - PackedVectorImpl vec; - vec.append(10); - vec.append(20); - vec.append(30); - vec.append(40); - vec.append(50); + VectorLike vec; + container_append(vec, 10); + container_append(vec, 20); + container_append(vec, 30); + container_append(vec, 40); + container_append(vec, 50); auto it = std::find(vec.begin(), vec.end(), 30); assert(it != vec.end()); @@ -2988,12 +3030,12 @@ void test_packed_vector_iterators() { // Test 8: Const iterator { - PackedVectorImpl vec; - vec.append(5); - vec.append(15); - vec.append(25); + VectorLike vec; + container_append(vec, 5); + container_append(vec, 15); + container_append(vec, 25); - const PackedVectorImpl& const_vec = vec; + const VectorLike& const_vec = vec; size_t count = 0; for (auto it = const_vec.begin(); it != const_vec.end(); ++it) { @@ -3009,9 +3051,9 @@ void test_packed_vector_iterators() { assert(*it == 25); } - // Test 9: Large vector with various patterns + // Test 9: Large container with various patterns { - PackedVectorImpl vec; + VectorLike vec; random_device rd; default_random_engine prng(rd()); uniform_int_distribution val_distr(0, 10000); @@ -3022,7 +3064,7 @@ void test_packed_vector_iterators() { for (size_t i = 0; i < num_elements; i++) { uint64_t val = val_distr(prng); expected.push_back(val); - vec.append(val); + container_append(vec, val); } size_t idx = 0; @@ -3035,10 +3077,10 @@ void test_packed_vector_iterators() { // Test 10: Iteration after modification { - PackedVectorImpl vec; - vec.append(1); - vec.append(2); - vec.append(3); + VectorLike vec; + container_append(vec, 1); + container_append(vec, 2); + container_append(vec, 3); // First iteration size_t count = 0; @@ -3048,7 +3090,7 @@ void test_packed_vector_iterators() { assert(count == 3); // Modify - vec.append(4); + container_append(vec, 4); vec.set(0, 100); // Second iteration @@ -3061,16 +3103,16 @@ void test_packed_vector_iterators() { assert(idx == 4); } - // Test 11: Bit-width changes during iteration + // Test 11: Value range during iteration (e.g., bit-width changes for PackedVector) { - PackedVectorImpl vec; - // Start with small values (low bit-width) - vec.append(1); - vec.append(2); - vec.append(3); + VectorLike vec; + // Start with small values + container_append(vec, 1); + container_append(vec, 2); + container_append(vec, 3); - // Add large value (increases bit-width) - vec.append(1000000); + // Add large value + container_append(vec, 1000000); vector expected = {1, 2, 3, 1000000}; size_t idx = 0; @@ -3083,9 +3125,9 @@ void test_packed_vector_iterators() { // Test 12: Iterator copy construction { - PackedVectorImpl vec; - vec.append(10); - vec.append(20); + VectorLike vec; + container_append(vec, 10); + container_append(vec, 20); auto it1 = vec.begin(); auto it2(it1); // Copy constructor @@ -3097,10 +3139,10 @@ void test_packed_vector_iterators() { // Test 13: Iterator assignment { - PackedVectorImpl vec; - vec.append(10); - vec.append(20); - vec.append(30); + VectorLike vec; + container_append(vec, 10); + container_append(vec, 20); + container_append(vec, 30); auto it1 = vec.begin(); auto it2 = vec.begin(); @@ -3113,6 +3155,12 @@ void test_packed_vector_iterators() { assert(it1 == it2); assert(*it1 == 20); } +} + +template +void test_packed_vector_iterators() { + // Run common tests + test_vector_like_iterators_common(); cerr << "PackedVector (" << typeid(PackedVectorImpl).name() << ") iterator tests successful!" << endl; } @@ -3213,87 +3261,10 @@ void test_paged_vector() { template void test_paged_vector_iterators() { - // Test 1: Empty vector iteration - { - PagedVectorImpl vec; - assert(vec.begin() == vec.end()); + // Run common tests + test_vector_like_iterators_common(); - size_t count = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - count++; - } - assert(count == 0); - } - - // Test 2: Single element - { - PagedVectorImpl vec; - vec.append(42); - - assert(vec.begin() != vec.end()); - - auto it = vec.begin(); - assert(*it == 42); - ++it; - assert(it == vec.end()); - } - - // Test 3: Multiple elements - basic iteration - { - PagedVectorImpl vec; - vector expected = {10, 20, 30, 40, 50}; - - for (auto val : expected) { - vec.append(val); - } - - // Iterate and compare - size_t idx = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - assert(idx < expected.size()); - assert(*it == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 4: Range-based for loop - { - PagedVectorImpl vec; - vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; - - for (auto val : expected) { - vec.append(val); - } - - size_t idx = 0; - for (auto val : vec) { - assert(idx < expected.size()); - assert(val == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 5: Iterator equality and inequality - { - PagedVectorImpl vec; - vec.append(1); - vec.append(2); - vec.append(3); - - auto it1 = vec.begin(); - auto it2 = vec.begin(); - assert(it1 == it2); - - ++it2; - assert(it1 != it2); - - ++it1; - assert(it1 == it2); - } - - // Test 6: Iteration over page boundaries (important for PagedVector) + // PagedVector-specific test: Iteration over page boundaries { PagedVectorImpl vec; // Create a temp vector to get page_width @@ -3311,155 +3282,6 @@ void test_paged_vector_iterators() { assert(idx == num_elements); } - // Test 7: std::distance compatibility - { - PagedVectorImpl vec; - for (size_t i = 0; i < 15; i++) { - vec.append(i); - } - - auto dist = std::distance(vec.begin(), vec.end()); - assert((size_t)dist == vec.size()); - assert((size_t)dist == 15); - } - - // Test 8: std::find compatibility - { - PagedVectorImpl vec; - vec.append(10); - vec.append(20); - vec.append(30); - vec.append(40); - vec.append(50); - - auto it = std::find(vec.begin(), vec.end(), 30); - assert(it != vec.end()); - assert(*it == 30); - - auto it2 = std::find(vec.begin(), vec.end(), 999); - assert(it2 == vec.end()); - } - - // Test 9: Const iterator - { - PagedVectorImpl vec; - vec.append(5); - vec.append(15); - vec.append(25); - - const PagedVectorImpl& const_vec = vec; - - size_t count = 0; - for (auto it = const_vec.begin(); it != const_vec.end(); ++it) { - count++; - } - assert(count == 3); - - auto it = const_vec.begin(); - assert(*it == 5); - ++it; - assert(*it == 15); - ++it; - assert(*it == 25); - } - - // Test 10: Large vector with various patterns - { - PagedVectorImpl vec; - random_device rd; - default_random_engine prng(rd()); - uniform_int_distribution val_distr(0, 10000); - - vector expected; - size_t num_elements = 200; - - for (size_t i = 0; i < num_elements; i++) { - uint64_t val = val_distr(prng); - expected.push_back(val); - vec.append(val); - } - - size_t idx = 0; - for (auto val : vec) { - assert(val == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 11: Iteration after modification - { - PagedVectorImpl vec; - vec.append(1); - vec.append(2); - vec.append(3); - - // First iteration - size_t count = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - count++; - } - assert(count == 3); - - // Modify - vec.append(4); - vec.set(0, 100); - - // Second iteration - vector expected = {100, 2, 3, 4}; - size_t idx = 0; - for (auto val : vec) { - assert(val == expected[idx]); - idx++; - } - assert(idx == 4); - } - - // Test 12: All zeros - { - PagedVectorImpl vec; - vec.append(0); - vec.append(0); - vec.append(0); - - for (auto val : vec) { - assert(val == 0); - } - } - - // Test 13: Iterator copy construction - { - PagedVectorImpl vec; - vec.append(10); - vec.append(20); - - auto it1 = vec.begin(); - auto it2(it1); // Copy constructor - - assert(it1 == it2); - assert(*it1 == *it2); - assert(*it1 == 10); - } - - // Test 14: Iterator assignment - { - PagedVectorImpl vec; - vec.append(10); - vec.append(20); - vec.append(30); - - auto it1 = vec.begin(); - auto it2 = vec.begin(); - ++it2; - - assert(*it1 == 10); - assert(*it2 == 20); - - it1 = it2; // Assignment - assert(it1 == it2); - assert(*it1 == 20); - } - cerr << "PagedVector (" << typeid(PagedVectorImpl).name() << ") iterator tests successful!" << endl; } From fed5ff9021581b33e017f3a29e8f84e83fa870ea Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 19 Nov 2025 17:42:25 -0500 Subject: [PATCH 5/9] Stop trying to use SFINAE and just change the API from append/pop to push_back/pop_back like the STL --- .../bdsg/internal/mapped_structs_1.cpp | 4 +- .../bdsg/internal/base_packed_graph.hpp | 166 ++-- bdsg/include/bdsg/internal/packed_structs.hpp | 44 +- bdsg/src/test_libbdsg.cpp | 731 ++---------------- 4 files changed, 172 insertions(+), 773 deletions(-) diff --git a/bdsg/cmake_bindings/bdsg/internal/mapped_structs_1.cpp b/bdsg/cmake_bindings/bdsg/internal/mapped_structs_1.cpp index 1435dda3..85e8dc63 100644 --- a/bdsg/cmake_bindings/bdsg/internal/mapped_structs_1.cpp +++ b/bdsg/cmake_bindings/bdsg/internal/mapped_structs_1.cpp @@ -57,8 +57,8 @@ void bind_bdsg_internal_mapped_structs_1(std::function< pybind11::module &(std:: cl.def("assign", (class bdsg::PackedDeque<> & (bdsg::PackedDeque::*)(const class bdsg::PackedDeque<> &)) &bdsg::PackedDeque<>::operator=, "C++: bdsg::PackedDeque<>::operator=(const class bdsg::PackedDeque<> &) --> class bdsg::PackedDeque<> &", pybind11::return_value_policy::automatic, pybind11::arg("other")); cl.def("set", (void (bdsg::PackedDeque::*)(const unsigned long &, const unsigned long &)) &bdsg::PackedDeque<>::set, "C++: bdsg::PackedDeque<>::set(const unsigned long &, const unsigned long &) --> void", pybind11::arg("i"), pybind11::arg("value")); cl.def("get", (unsigned long (bdsg::PackedDeque::*)(const unsigned long &) const) &bdsg::PackedDeque<>::get, "C++: bdsg::PackedDeque<>::get(const unsigned long &) const --> unsigned long", pybind11::arg("i")); - cl.def("append_front", (void (bdsg::PackedDeque::*)(const unsigned long &)) &bdsg::PackedDeque<>::append_front, "C++: bdsg::PackedDeque<>::append_front(const unsigned long &) --> void", pybind11::arg("value")); - cl.def("append_back", (void (bdsg::PackedDeque::*)(const unsigned long &)) &bdsg::PackedDeque<>::append_back, "C++: bdsg::PackedDeque<>::append_back(const unsigned long &) --> void", pybind11::arg("value")); + cl.def("push_front", (void (bdsg::PackedDeque::*)(const unsigned long &)) &bdsg::PackedDeque<>::push_front, "C++: bdsg::PackedDeque<>::push_front(const unsigned long &) --> void", pybind11::arg("value")); + cl.def("push_back", (void (bdsg::PackedDeque::*)(const unsigned long &)) &bdsg::PackedDeque<>::push_back, "C++: bdsg::PackedDeque<>::push_back(const unsigned long &) --> void", pybind11::arg("value")); cl.def("pop_front", (void (bdsg::PackedDeque::*)()) &bdsg::PackedDeque<>::pop_front, "C++: bdsg::PackedDeque<>::pop_front() --> void"); cl.def("pop_back", (void (bdsg::PackedDeque::*)()) &bdsg::PackedDeque<>::pop_back, "C++: bdsg::PackedDeque<>::pop_back() --> void"); cl.def("reserve", (void (bdsg::PackedDeque::*)(const unsigned long &)) &bdsg::PackedDeque<>::reserve, "C++: bdsg::PackedDeque<>::reserve(const unsigned long &) --> void", pybind11::arg("future_size")); diff --git a/bdsg/include/bdsg/internal/base_packed_graph.hpp b/bdsg/include/bdsg/internal/base_packed_graph.hpp index dd152ac4..5b11d4d7 100644 --- a/bdsg/include/bdsg/internal/base_packed_graph.hpp +++ b/bdsg/include/bdsg/internal/base_packed_graph.hpp @@ -1160,26 +1160,26 @@ size_t BasePackedGraph::new_node_record(nid_t node_id) { size_t next_g_iv_idx = graph_iv.size(); // no edges yet, null pointer for linked list - graph_iv.append(0); - graph_iv.append(0); + graph_iv.push_back(0); + graph_iv.push_back(0); // record the sequence interval - seq_start_iv.append(0); - seq_length_iv.append(0); + seq_start_iv.push_back(0); + seq_length_iv.push_back(0); // initialize an empty path membership list - path_membership_node_iv.append(0); + path_membership_node_iv.push_back(0); // expand the ID vector's dimensions so it can handle the full ID interval if (nid_to_graph_iv.empty()) { - nid_to_graph_iv.append_back(0); + nid_to_graph_iv.push_back(0); } else { for (int64_t i = node_id; i < min_id; i++) { - nid_to_graph_iv.append_front(0); + nid_to_graph_iv.push_front(0); } for (int64_t i = nid_to_graph_iv.size(); i <= node_id - min_id; i++) { - nid_to_graph_iv.append_back(0); + nid_to_graph_iv.push_back(0); } } @@ -1215,7 +1215,7 @@ handle_t BasePackedGraph::create_handle(const string& sequence, const n // encode the sequence interval for (size_t i = 0; i < sequence.size(); i++) { - seq_iv.append(encode_nucleotide(sequence[i])); + seq_iv.push_back(encode_nucleotide(sequence[i])); } return get_handle(id); @@ -1243,8 +1243,8 @@ void BasePackedGraph::create_edge(const handle_t& left, const handle_t& GRAPH_START_EDGES_OFFSET); // add a new linked list node pointing to the rest of the list - edge_lists_iv.append(encode_traversal(right)); - edge_lists_iv.append(graph_iv.get(g_iv_left)); + edge_lists_iv.push_back(encode_traversal(right)); + edge_lists_iv.push_back(graph_iv.get(g_iv_left)); // make this new node the head graph_iv.set(g_iv_left, edge_lists_iv.size() / EDGE_RECORD_SIZE); @@ -1255,8 +1255,8 @@ void BasePackedGraph::create_edge(const handle_t& left, const handle_t& } // add a new linked list node pointing to the rest of the list - edge_lists_iv.append(encode_traversal(flip(left))); - edge_lists_iv.append(graph_iv.get(g_iv_right)); + edge_lists_iv.push_back(encode_traversal(flip(left))); + edge_lists_iv.push_back(graph_iv.get(g_iv_right)); // make this new node the head graph_iv.set(g_iv_right, edge_lists_iv.size() / EDGE_RECORD_SIZE); } @@ -1570,14 +1570,14 @@ std::vector BasePackedGraph::divide_handle(const handle_t& ha seq_length_iv.set(graph_index_to_seq_len_index(g_iv_idx), off - last_offset); // create an edge forward onto the new node - edge_lists_iv.append(encode_traversal(get_handle(next_id))); - edge_lists_iv.append(0); + edge_lists_iv.push_back(encode_traversal(get_handle(next_id))); + edge_lists_iv.push_back(0); // add the edge onto the previous node graph_iv.set(g_iv_idx + GRAPH_END_EDGES_OFFSET, edge_lists_iv.size() / EDGE_RECORD_SIZE); // create an edge backward to the previous node - edge_lists_iv.append(encode_traversal(get_handle(prev_id, true))); - edge_lists_iv.append(0); + edge_lists_iv.push_back(encode_traversal(get_handle(prev_id, true))); + edge_lists_iv.push_back(0); // add the edge backwards to the current node graph_iv.set(new_g_iv_idx + GRAPH_START_EDGES_OFFSET, edge_lists_iv.size() / EDGE_RECORD_SIZE); @@ -1635,17 +1635,17 @@ std::vector BasePackedGraph::divide_handle(const handle_t& ha vector divided_trav_offsets{occ_idx}; for (size_t i = 1; i < return_val.size(); i++) { // the new traversals will have the same strandedness as the original occurrence - packed_path.steps_iv.append(encode_traversal(path_trav_rev ? flip(return_val[i]) : return_val[i])); - packed_path.links_iv.append(0); - packed_path.links_iv.append(0); + packed_path.steps_iv.push_back(encode_traversal(path_trav_rev ? flip(return_val[i]) : return_val[i])); + packed_path.links_iv.push_back(0); + packed_path.links_iv.push_back(0); divided_trav_offsets.push_back(packed_path.steps_iv.size() / STEP_RECORD_SIZE); // record the membership of this node in this path size_t node_member_idx = graph_index_to_node_member_index(graph_iv_index(return_val[i])); - path_membership_id_iv.append(get_membership_path(path_membership)); - path_membership_offset_iv.append(packed_path.steps_iv.size() / STEP_RECORD_SIZE); - path_membership_next_iv.append(path_membership_node_iv.get(node_member_idx)); + path_membership_id_iv.push_back(get_membership_path(path_membership)); + path_membership_offset_iv.push_back(packed_path.steps_iv.size() / STEP_RECORD_SIZE); + path_membership_next_iv.push_back(path_membership_node_iv.get(node_member_idx)); // make this new membership record the head of the linked list path_membership_node_iv.set(node_member_idx, path_membership_next_iv.size() / MEMBERSHIP_NEXT_RECORD_SIZE); @@ -1774,7 +1774,7 @@ handle_t BasePackedGraph::change_sequence(const handle_t& handle, const // the new sequence doesn't fit, add it at the end seq_start_iv.set(graph_index_to_seq_start_index(g_iv_index), seq_iv.size()); for (size_t i = 0; i < sequence.size(); ++i) { - seq_iv.append(encode_nucleotide(sequence[i])); + seq_iv.push_back(encode_nucleotide(sequence[i])); } deleted_bases += seq_len; } @@ -1876,9 +1876,9 @@ void BasePackedGraph::defragment_path(const int64_t& path_idx, bool for size_t prev = 0; while (copying_from != 0 && (first_iter || copying_from != path_head_iv.get(path_idx))) { // make a new record - new_steps_iv.append(get_step_trav(path, copying_from)); - new_links_iv.append(prev); - new_links_iv.append(0); + new_steps_iv.push_back(get_step_trav(path, copying_from)); + new_links_iv.push_back(prev); + new_links_iv.push_back(0); size_t here = new_steps_iv.size() / STEP_RECORD_SIZE; @@ -1915,7 +1915,7 @@ void BasePackedGraph::defragment_path(const int64_t& path_idx, bool for // records (even if the node occurs multiple times on this path), so we will use a bit deque // indexed by node_id - min_id to flag nodes as either translated or untranslated PackedDeque<> nid_translated; - nid_translated.append_back(0); + nid_translated.push_back(0); nid_t min_translated_id = get_id(decode_traversal(get_step_trav(path, path_head_iv.get(path_idx)))); first_iter = true; @@ -1929,13 +1929,13 @@ void BasePackedGraph::defragment_path(const int64_t& path_idx, bool for // expand the bounds of the deque as necessary to be able to index by ID if (step_node_id < min_translated_id) { for (nid_t i = step_node_id; i < min_translated_id; ++i) { - nid_translated.append_front(0); + nid_translated.push_front(0); } min_translated_id = step_node_id; } else if (step_node_id >= min_translated_id + nid_translated.size()) { for (nid_t i = min_translated_id + nid_translated.size(); i <= step_node_id; ++i) { - nid_translated.append_back(0); + nid_translated.push_back(0); } } @@ -2048,49 +2048,49 @@ void BasePackedGraph::eject_deleted_paths() { decltype(path_name_start_iv) new_path_name_start_iv; new_path_name_start_iv.reserve(paths.size()); for (size_t i = 0; i < paths.size(); ++i) { - new_path_name_start_iv.append(path_name_start_iv.get(i)); + new_path_name_start_iv.push_back(path_name_start_iv.get(i)); } path_name_start_iv = std::move(new_path_name_start_iv); PackedVector<> new_path_name_length_iv; new_path_name_length_iv.reserve(paths.size()); for (size_t i = 0; i < paths.size(); ++i) { - new_path_name_length_iv.append(path_name_length_iv.get(i)); + new_path_name_length_iv.push_back(path_name_length_iv.get(i)); } path_name_length_iv = std::move(new_path_name_length_iv); PackedVector<> new_path_is_deleted_iv; new_path_is_deleted_iv.reserve(paths.size()); for (size_t i = 0; i < paths.size(); ++i) { - new_path_is_deleted_iv.append(path_is_deleted_iv.get(i)); + new_path_is_deleted_iv.push_back(path_is_deleted_iv.get(i)); } path_is_deleted_iv = std::move(new_path_is_deleted_iv); PackedVector<> new_path_is_circular_iv; new_path_is_circular_iv.reserve(paths.size()); for (size_t i = 0; i < paths.size(); ++i) { - new_path_is_circular_iv.append(path_is_circular_iv.get(i)); + new_path_is_circular_iv.push_back(path_is_circular_iv.get(i)); } path_is_circular_iv = std::move(new_path_is_circular_iv); decltype(path_head_iv) new_path_head_iv; new_path_head_iv.reserve(paths.size()); for (size_t i = 0; i < paths.size(); ++i) { - new_path_head_iv.append(path_head_iv.get(i)); + new_path_head_iv.push_back(path_head_iv.get(i)); } path_head_iv = std::move(new_path_head_iv); decltype(path_tail_iv) new_path_tail_iv; new_path_tail_iv.reserve(paths.size()); for (size_t i = 0; i < paths.size(); ++i) { - new_path_tail_iv.append(path_tail_iv.get(i)); + new_path_tail_iv.push_back(path_tail_iv.get(i)); } path_tail_iv = std::move(new_path_tail_iv); PackedVector<> new_path_deleted_steps_iv; new_path_deleted_steps_iv.reserve(paths.size()); for (size_t i = 0; i < paths.size(); ++i) { - new_path_deleted_steps_iv.append(path_deleted_steps_iv.get(i)); + new_path_deleted_steps_iv.push_back(path_deleted_steps_iv.get(i)); } path_deleted_steps_iv = std::move(new_path_deleted_steps_iv); @@ -2140,7 +2140,7 @@ void BasePackedGraph::tighten() { new_nid_to_graph_iv.reserve(nid_to_graph_iv.size()); // transfer of the data for (size_t i = 0; i < nid_to_graph_iv.size(); i++) { - new_nid_to_graph_iv.append_back(nid_to_graph_iv.get(i)); + new_nid_to_graph_iv.push_back(nid_to_graph_iv.get(i)); } // replace the old one nid_to_graph_iv = std::move(new_nid_to_graph_iv); @@ -2161,7 +2161,7 @@ void BasePackedGraph::tighten() { seq_start_iv.set(i, new_seq_iv.size()); // transfer the actual sequence over for (size_t j = begin; j < end; j++) { - new_seq_iv.append(seq_iv.get(j)); + new_seq_iv.push_back(seq_iv.get(j)); } } // replace the old seq iv @@ -2211,11 +2211,11 @@ void BasePackedGraph::defragment(bool force) { if (raw_g_iv_idx) { size_t g_iv_idx = (raw_g_iv_idx - 1) * GRAPH_RECORD_SIZE; // this node still exists, create a new copy - new_graph_iv.append(graph_iv.get(g_iv_idx + GRAPH_START_EDGES_OFFSET)); - new_graph_iv.append(graph_iv.get(g_iv_idx + GRAPH_END_EDGES_OFFSET)); - new_seq_length_iv.append(seq_length_iv.get(graph_index_to_seq_len_index(g_iv_idx))); - new_seq_start_iv.append(seq_start_iv.get(graph_index_to_seq_start_index(g_iv_idx))); - new_path_membership_node_iv.append(path_membership_node_iv.get(graph_index_to_node_member_index(g_iv_idx))); + new_graph_iv.push_back(graph_iv.get(g_iv_idx + GRAPH_START_EDGES_OFFSET)); + new_graph_iv.push_back(graph_iv.get(g_iv_idx + GRAPH_END_EDGES_OFFSET)); + new_seq_length_iv.push_back(seq_length_iv.get(graph_index_to_seq_len_index(g_iv_idx))); + new_seq_start_iv.push_back(seq_start_iv.get(graph_index_to_seq_start_index(g_iv_idx))); + new_path_membership_node_iv.push_back(path_membership_node_iv.get(graph_index_to_node_member_index(g_iv_idx))); // update the pointer into graph_iv nid_to_graph_iv.set(i, new_graph_iv.size() / GRAPH_RECORD_SIZE); } @@ -2249,16 +2249,16 @@ void BasePackedGraph::defragment(bool force) { size_t edge_list_idx = graph_iv.get(g_iv_idx + edge_list_offset); if (edge_list_idx) { // add a new edge record - new_edge_lists_iv.append(get_edge_target(edge_list_idx)); - new_edge_lists_iv.append(0); + new_edge_lists_iv.push_back(get_edge_target(edge_list_idx)); + new_edge_lists_iv.push_back(0); // point the graph vector at this new edge list graph_iv.set(g_iv_idx + edge_list_offset, new_edge_lists_iv.size() / EDGE_RECORD_SIZE); edge_list_idx = get_next_edge_index(edge_list_idx); while (edge_list_idx) { // add a new edge record - new_edge_lists_iv.append(get_edge_target(edge_list_idx)); - new_edge_lists_iv.append(0); + new_edge_lists_iv.push_back(get_edge_target(edge_list_idx)); + new_edge_lists_iv.push_back(0); // point the previous link at this one new_edge_lists_iv.set(new_edge_lists_iv.size() - 2 * EDGE_RECORD_SIZE + EDGE_NEXT_OFFSET, new_edge_lists_iv.size() / EDGE_RECORD_SIZE); @@ -2298,9 +2298,9 @@ void BasePackedGraph::defragment(bool force) { uint64_t member_idx = path_membership_node_iv.get(graph_index_to_node_member_index(g_iv_idx)); if (member_idx) { // make a new membership record - new_path_membership_id_iv.append(get_membership_path(member_idx)); - new_path_membership_offset_iv.append(get_membership_step(member_idx)); - new_path_membership_next_iv.append(0); + new_path_membership_id_iv.push_back(get_membership_path(member_idx)); + new_path_membership_offset_iv.push_back(get_membership_step(member_idx)); + new_path_membership_next_iv.push_back(0); // point the membership vector here path_membership_node_iv.set(graph_index_to_node_member_index(g_iv_idx), @@ -2309,9 +2309,9 @@ void BasePackedGraph::defragment(bool force) { member_idx = get_next_membership(member_idx); while (member_idx) { // make a new membership record - new_path_membership_id_iv.append(get_membership_path(member_idx)); - new_path_membership_offset_iv.append(get_membership_step(member_idx)); - new_path_membership_next_iv.append(0); + new_path_membership_id_iv.push_back(get_membership_path(member_idx)); + new_path_membership_offset_iv.push_back(get_membership_step(member_idx)); + new_path_membership_next_iv.push_back(0); // point the previous link at this one new_path_membership_next_iv.set(new_path_membership_next_iv.size() - 2 * MEMBERSHIP_NEXT_RECORD_SIZE, new_path_membership_next_iv.size() / MEMBERSHIP_NEXT_RECORD_SIZE); @@ -2617,7 +2617,7 @@ PackedVector<> BasePackedGraph::encode_path_name(const string& path_nam template void BasePackedGraph::append_path_name(const string& path_name) { for (size_t i = 0; i < path_name.size(); ++i) { - path_names_iv.append(get_or_make_assignment(path_name.at(i))); + path_names_iv.push_back(get_or_make_assignment(path_name.at(i))); } } @@ -2748,13 +2748,13 @@ path_handle_t BasePackedGraph::create_path_handle(const string& name, b paths.emplace_back(); - path_name_start_iv.append(path_names_iv.size()); - path_name_length_iv.append(name.size()); - path_is_circular_iv.append(is_circular); - path_is_deleted_iv.append(false); - path_head_iv.append(0); - path_tail_iv.append(0); - path_deleted_steps_iv.append(0); + path_name_start_iv.push_back(path_names_iv.size()); + path_name_length_iv.push_back(name.size()); + path_is_circular_iv.push_back(is_circular); + path_is_deleted_iv.push_back(false); + path_head_iv.push_back(0); + path_tail_iv.push_back(0); + path_deleted_steps_iv.push_back(0); append_path_name(name); @@ -2767,9 +2767,9 @@ step_handle_t BasePackedGraph::append_step(const path_handle_t& path, c PackedPath& packed_path = paths.at(as_integer(path)); // create a new path record - packed_path.steps_iv.append(as_integer(to_append)); - packed_path.links_iv.append(path_tail_iv.get(as_integer(path))); - packed_path.links_iv.append(0); + packed_path.steps_iv.push_back(as_integer(to_append)); + packed_path.links_iv.push_back(path_tail_iv.get(as_integer(path))); + packed_path.links_iv.push_back(0); // the offset associated with the new record size_t step_offset = packed_path.steps_iv.size() / STEP_RECORD_SIZE; @@ -2793,9 +2793,9 @@ step_handle_t BasePackedGraph::append_step(const path_handle_t& path, c // record the membership of this node in this path size_t node_member_idx = graph_index_to_node_member_index(graph_iv_index(to_append)); - path_membership_id_iv.append(as_integer(path)); - path_membership_offset_iv.append(step_offset); - path_membership_next_iv.append(path_membership_node_iv.get(node_member_idx)); + path_membership_id_iv.push_back(as_integer(path)); + path_membership_offset_iv.push_back(step_offset); + path_membership_next_iv.push_back(path_membership_node_iv.get(node_member_idx)); // make this new membership record the head of the linked list path_membership_node_iv.set(node_member_idx, path_membership_next_iv.size() / MEMBERSHIP_NEXT_RECORD_SIZE); @@ -2813,9 +2813,9 @@ step_handle_t BasePackedGraph::prepend_step(const path_handle_t& path, PackedPath& packed_path = paths.at(as_integer(path)); // create a new path record - packed_path.steps_iv.append(as_integer(to_prepend)); - packed_path.links_iv.append(0); - packed_path.links_iv.append(path_head_iv.get(as_integer(path))); + packed_path.steps_iv.push_back(as_integer(to_prepend)); + packed_path.links_iv.push_back(0); + packed_path.links_iv.push_back(path_head_iv.get(as_integer(path))); // the offset associated with the new record size_t step_offset = packed_path.steps_iv.size() / STEP_RECORD_SIZE; @@ -2839,9 +2839,9 @@ step_handle_t BasePackedGraph::prepend_step(const path_handle_t& path, // record the membership of this node in this path size_t node_member_idx = graph_index_to_node_member_index(graph_iv_index(to_prepend)); - path_membership_id_iv.append(as_integer(path)); - path_membership_offset_iv.append(step_offset); - path_membership_next_iv.append(path_membership_node_iv.get(node_member_idx)); + path_membership_id_iv.push_back(as_integer(path)); + path_membership_offset_iv.push_back(step_offset); + path_membership_next_iv.push_back(path_membership_node_iv.get(node_member_idx)); // make this new membership record the head of the linked list path_membership_node_iv.set(node_member_idx, path_membership_next_iv.size() / MEMBERSHIP_NEXT_RECORD_SIZE); @@ -2938,9 +2938,9 @@ pair BasePackedGraph::rewrite_segment(con for (const handle_t& handle : new_segment) { // create a new step record - packed_path.steps_iv.append(encode_traversal(handle)); - packed_path.links_iv.append(0); - packed_path.links_iv.append(0); + packed_path.steps_iv.push_back(encode_traversal(handle)); + packed_path.links_iv.push_back(0); + packed_path.links_iv.push_back(0); uint64_t step_offset = packed_path.steps_iv.size() / STEP_RECORD_SIZE; @@ -2982,9 +2982,9 @@ pair BasePackedGraph::rewrite_segment(con // put a membership record for this occurrence at the front of the membership list size_t node_member_idx = graph_index_to_node_member_index(graph_iv_index(handle)); - path_membership_id_iv.append(as_integers(segment_end)[0]); - path_membership_offset_iv.append(step_offset); - path_membership_next_iv.append(path_membership_node_iv.get(node_member_idx)); + path_membership_id_iv.push_back(as_integers(segment_end)[0]); + path_membership_offset_iv.push_back(step_offset); + path_membership_next_iv.push_back(path_membership_node_iv.get(node_member_idx)); path_membership_node_iv.set(node_member_idx, path_membership_next_iv.size() / MEMBERSHIP_NEXT_RECORD_SIZE); if (first_iter) { @@ -3084,14 +3084,14 @@ void BasePackedGraph::reassign_node_ids(const std::function::get(const size_t& i) const { } template -inline void PackedVector::append(const uint64_t& value) { +inline void PackedVector::push_back(const uint64_t& value) { resize(filled + 1); set(filled - 1, value); } template -inline void PackedVector::pop() { +inline void PackedVector::pop_back() { resize(filled - 1); } @@ -1041,7 +1041,7 @@ inline void PackedDeque::reserve(const size_t& future_size) { } template -inline void PackedDeque::append_front(const uint64_t& value) { +inline void PackedDeque::push_front(const uint64_t& value) { // expand capacity if necessary if (filled == vec.size()) { size_t new_capacity = size_t(factor * vec.size()) + 1; @@ -1063,7 +1063,7 @@ inline void PackedDeque::append_front(const uint64_t& value) { } template -inline void PackedDeque::append_back(const uint64_t& value) { +inline void PackedDeque::push_back(const uint64_t& value) { // expand capacity if necessary if (filled == vec.size()) { size_t new_capacity = size_t(factor * vec.size()) + 1; @@ -1262,12 +1262,12 @@ inline uint64_t PagedVector::get(const size_t& i) const { } template -inline void PagedVector::append(const uint64_t& value) { +inline void PagedVector::push_back(const uint64_t& value) { if (filled == pages.size() * page_size) { // init a new page and a new anchor pages.emplace_back(); pages.back().resize(page_size); - anchors.append(0); + anchors.push_back(0); } // use the logic in set to choose anchor and diff @@ -1276,12 +1276,12 @@ inline void PagedVector::append(const uint64_t& value) { } template -inline void PagedVector::pop() { +inline void PagedVector::pop_back() { filled--; while (filled + page_size <= pages.size() * page_size) { // the final page is unused now, remove it pages.pop_back(); // TODO: this won't resize since it's an STL vector - anchors.pop(); + anchors.pop_back(); } } @@ -1496,22 +1496,22 @@ inline uint64_t RobustPagedVector::get(const size_t& i) cons } template -inline void RobustPagedVector::append(const uint64_t& value) { +inline void RobustPagedVector::push_back(const uint64_t& value) { if (first_page.size() < latter_pages.page_width()) { - first_page.append(value); + first_page.push_back(value); } else { - latter_pages.append(value); + latter_pages.push_back(value); } } template -inline void RobustPagedVector::pop() { +inline void RobustPagedVector::pop_back() { if (latter_pages.empty()) { - first_page.pop(); + first_page.pop_back(); } else { - latter_pages.pop(); + latter_pages.pop_back(); } } diff --git a/bdsg/src/test_libbdsg.cpp b/bdsg/src/test_libbdsg.cpp index 13b1b83d..9da2e186 100644 --- a/bdsg/src/test_libbdsg.cpp +++ b/bdsg/src/test_libbdsg.cpp @@ -2833,7 +2833,7 @@ void test_packed_vector() { case APPEND: for (size_t k = 0; k < appends_per_op; k++) { std_vec.push_back(next_val); - dyn_vec.append(next_val); + dyn_vec.push_back(next_val); next_val++; } @@ -2843,7 +2843,7 @@ void test_packed_vector() { if (!std_vec.empty()) { for (size_t k = 0; k < pops_per_op; k++) { std_vec.pop_back(); - dyn_vec.pop(); + dyn_vec.pop_back(); } } @@ -2875,51 +2875,13 @@ void test_packed_vector() { cerr << "PackedVector (" << typeid(PackedVectorImpl).name() << ") tests successful!" << endl; } -// SFINAE helpers to detect append() vs append_back() -template -class has_append_method { - template - static auto test(int) -> decltype(std::declval().append(0), std::true_type()); - - template - static std::false_type test(...); - -public: - static constexpr bool value = decltype(test(0))::value; -}; - -template -class has_append_back_method { - template - static auto test(int) -> decltype(std::declval().append_back(0), std::true_type()); - - template - static std::false_type test(...); - -public: - static constexpr bool value = decltype(test(0))::value; -}; - -// Generic helper to append to a container (works with both append() and append_back()) -template -typename std::enable_if::value>::type -container_append(Container& c, uint64_t val) { - c.append(val); -} - -template -typename std::enable_if::value && !has_append_method::value>::type -container_append(Container& c, uint64_t val) { - c.append_back(val); -} - /** * Generic iterator test function that works with any vector-like container * (PackedVector, PagedVector, RobustPagedVector, PackedDeque) */ template -void test_vector_like_iterators_common() { - // Test 1: Empty iteration +void test_iterators() { + // Empty iteration { VectorLike vec; assert(vec.begin() == vec.end()); @@ -2931,10 +2893,10 @@ void test_vector_like_iterators_common() { assert(count == 0); } - // Test 2: Single element + // Single element { VectorLike vec; - container_append(vec, 42); + vec.push_back(42); assert(vec.begin() != vec.end()); @@ -2944,13 +2906,13 @@ void test_vector_like_iterators_common() { assert(it == vec.end()); } - // Test 3: Multiple elements - basic iteration + // Multiple elements - basic iteration { VectorLike vec; vector expected = {10, 20, 30, 40, 50}; for (auto val : expected) { - container_append(vec, val); + vec.push_back(val); } // Iterate and compare @@ -2963,13 +2925,13 @@ void test_vector_like_iterators_common() { assert(idx == expected.size()); } - // Test 4: Range-based for loop + // Range-based for loop { VectorLike vec; vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; for (auto val : expected) { - container_append(vec, val); + vec.push_back(val); } size_t idx = 0; @@ -2981,12 +2943,12 @@ void test_vector_like_iterators_common() { assert(idx == expected.size()); } - // Test 5: Iterator equality and inequality + // Iterator equality and inequality { VectorLike vec; - container_append(vec, 1); - container_append(vec, 2); - container_append(vec, 3); + vec.push_back(1); + vec.push_back(2); + vec.push_back(3); auto it1 = vec.begin(); auto it2 = vec.begin(); @@ -2999,11 +2961,11 @@ void test_vector_like_iterators_common() { assert(it1 == it2); } - // Test 6: std::distance compatibility + // std::distance compatibility { VectorLike vec; for (size_t i = 0; i < 15; i++) { - container_append(vec, i); + vec.push_back(i); } auto dist = std::distance(vec.begin(), vec.end()); @@ -3011,14 +2973,14 @@ void test_vector_like_iterators_common() { assert((size_t)dist == 15); } - // Test 7: std::find compatibility + // std::find compatibility { VectorLike vec; - container_append(vec, 10); - container_append(vec, 20); - container_append(vec, 30); - container_append(vec, 40); - container_append(vec, 50); + vec.push_back(10); + vec.push_back(20); + vec.push_back(30); + vec.push_back(40); + vec.push_back(50); auto it = std::find(vec.begin(), vec.end(), 30); assert(it != vec.end()); @@ -3028,12 +2990,12 @@ void test_vector_like_iterators_common() { assert(it2 == vec.end()); } - // Test 8: Const iterator + // Const iterator { VectorLike vec; - container_append(vec, 5); - container_append(vec, 15); - container_append(vec, 25); + vec.push_back(5); + vec.push_back(15); + vec.push_back(25); const VectorLike& const_vec = vec; @@ -3051,7 +3013,7 @@ void test_vector_like_iterators_common() { assert(*it == 25); } - // Test 9: Large container with various patterns + // Large container with various patterns { VectorLike vec; random_device rd; @@ -3064,7 +3026,7 @@ void test_vector_like_iterators_common() { for (size_t i = 0; i < num_elements; i++) { uint64_t val = val_distr(prng); expected.push_back(val); - container_append(vec, val); + vec.push_back(val); } size_t idx = 0; @@ -3075,12 +3037,12 @@ void test_vector_like_iterators_common() { assert(idx == expected.size()); } - // Test 10: Iteration after modification + // Iteration after modification { VectorLike vec; - container_append(vec, 1); - container_append(vec, 2); - container_append(vec, 3); + vec.push_back(1); + vec.push_back(2); + vec.push_back(3); // First iteration size_t count = 0; @@ -3090,7 +3052,7 @@ void test_vector_like_iterators_common() { assert(count == 3); // Modify - container_append(vec, 4); + vec.push_back(4); vec.set(0, 100); // Second iteration @@ -3103,31 +3065,11 @@ void test_vector_like_iterators_common() { assert(idx == 4); } - // Test 11: Value range during iteration (e.g., bit-width changes for PackedVector) + // Iterator copy construction { VectorLike vec; - // Start with small values - container_append(vec, 1); - container_append(vec, 2); - container_append(vec, 3); - - // Add large value - container_append(vec, 1000000); - - vector expected = {1, 2, 3, 1000000}; - size_t idx = 0; - for (auto val : vec) { - assert(val == expected[idx]); - idx++; - } - assert(idx == 4); - } - - // Test 12: Iterator copy construction - { - VectorLike vec; - container_append(vec, 10); - container_append(vec, 20); + vec.push_back(10); + vec.push_back(20); auto it1 = vec.begin(); auto it2(it1); // Copy constructor @@ -3137,12 +3079,12 @@ void test_vector_like_iterators_common() { assert(*it1 == 10); } - // Test 13: Iterator assignment + // Iterator assignment { VectorLike vec; - container_append(vec, 10); - container_append(vec, 20); - container_append(vec, 30); + vec.push_back(10); + vec.push_back(20); + vec.push_back(30); auto it1 = vec.begin(); auto it2 = vec.begin(); @@ -3155,14 +3097,8 @@ void test_vector_like_iterators_common() { assert(it1 == it2); assert(*it1 == 20); } -} - -template -void test_packed_vector_iterators() { - // Run common tests - test_vector_like_iterators_common(); - cerr << "PackedVector (" << typeid(PackedVectorImpl).name() << ") iterator tests successful!" << endl; + cerr << "Iterator (" << typeid(typename VectorLike::iterator).name() << ") tests successful!" << endl; } template @@ -3217,7 +3153,7 @@ void test_paged_vector() { case APPEND: for (size_t k = 0; k < appends_per_op; k++) { std_vec.push_back(next_val); - dyn_vec.append(next_val); + dyn_vec.push_back(next_val); next_val = val_distr(prng); } @@ -3227,7 +3163,7 @@ void test_paged_vector() { if (!std_vec.empty()) { for (size_t k = 0; k < pops_per_op; k++) { std_vec.pop_back(); - dyn_vec.pop(); + dyn_vec.pop_back(); } } @@ -3259,273 +3195,6 @@ void test_paged_vector() { cerr << "PagedVector (" << typeid(PagedVectorImpl).name() << ") tests successful!" << endl; } -template -void test_paged_vector_iterators() { - // Run common tests - test_vector_like_iterators_common(); - - // PagedVector-specific test: Iteration over page boundaries - { - PagedVectorImpl vec; - // Create a temp vector to get page_width - size_t num_elements = vec.page_width() * 3 + 5; // Cross multiple pages - - for (size_t i = 0; i < num_elements; i++) { - vec.append(i * 7); // Some pattern - } - - size_t idx = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - assert(*it == idx * 7); - idx++; - } - assert(idx == num_elements); - } - - cerr << "PagedVector (" << typeid(PagedVectorImpl).name() << ") iterator tests successful!" << endl; -} - -template -void test_robust_paged_vector_iterators() { - // Test 1: Empty vector iteration - { - RobustPagedVectorImpl vec; - assert(vec.begin() == vec.end()); - - size_t count = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - count++; - } - assert(count == 0); - } - - // Test 2: Single element - { - RobustPagedVectorImpl vec; - vec.append(42); - - assert(vec.begin() != vec.end()); - - auto it = vec.begin(); - assert(*it == 42); - ++it; - assert(it == vec.end()); - } - - // Test 3: Multiple elements - basic iteration - { - RobustPagedVectorImpl vec; - vector expected = {10, 20, 30, 40, 50}; - - for (auto val : expected) { - vec.append(val); - } - - // Iterate and compare - size_t idx = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - assert(idx < expected.size()); - assert(*it == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 4: Range-based for loop - { - RobustPagedVectorImpl vec; - vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; - - for (auto val : expected) { - vec.append(val); - } - - size_t idx = 0; - for (auto val : vec) { - assert(idx < expected.size()); - assert(val == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 5: Iterator equality and inequality - { - RobustPagedVectorImpl vec; - vec.append(1); - vec.append(2); - vec.append(3); - - auto it1 = vec.begin(); - auto it2 = vec.begin(); - assert(it1 == it2); - - ++it2; - assert(it1 != it2); - - ++it1; - assert(it1 == it2); - } - - // Test 6: Iteration across first_page/latter_pages boundary - { - RobustPagedVectorImpl vec; - // Get page width and add elements before, at, and after the boundary - size_t page_width = vec.page_width(); - - // Add elements spanning the boundary - for (size_t i = 0; i < page_width + 10; i++) { - vec.append(i * 3); - } - - size_t idx = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - assert(*it == idx * 3); - idx++; - } - assert(idx == page_width + 10); - } - - // Test 7: std::distance compatibility - { - RobustPagedVectorImpl vec; - for (size_t i = 0; i < 15; i++) { - vec.append(i); - } - - auto dist = std::distance(vec.begin(), vec.end()); - assert((size_t)dist == vec.size()); - assert((size_t)dist == 15); - } - - // Test 8: std::find compatibility - { - RobustPagedVectorImpl vec; - vec.append(10); - vec.append(20); - vec.append(30); - vec.append(40); - vec.append(50); - - auto it = std::find(vec.begin(), vec.end(), 30); - assert(it != vec.end()); - assert(*it == 30); - - auto it2 = std::find(vec.begin(), vec.end(), 999); - assert(it2 == vec.end()); - } - - // Test 9: Const iterator - { - RobustPagedVectorImpl vec; - vec.append(5); - vec.append(15); - vec.append(25); - - const RobustPagedVectorImpl& const_vec = vec; - - size_t count = 0; - for (auto it = const_vec.begin(); it != const_vec.end(); ++it) { - count++; - } - assert(count == 3); - - auto it = const_vec.begin(); - assert(*it == 5); - ++it; - assert(*it == 15); - ++it; - assert(*it == 25); - } - - // Test 10: Large vector spanning multiple pages - { - RobustPagedVectorImpl vec; - random_device rd; - default_random_engine prng(rd()); - uniform_int_distribution val_distr(0, 10000); - - vector expected; - size_t num_elements = 200; - - for (size_t i = 0; i < num_elements; i++) { - uint64_t val = val_distr(prng); - expected.push_back(val); - vec.append(val); - } - - size_t idx = 0; - for (auto val : vec) { - assert(val == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 11: Iteration after modification - { - RobustPagedVectorImpl vec; - vec.append(1); - vec.append(2); - vec.append(3); - - // First iteration - size_t count = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - count++; - } - assert(count == 3); - - // Modify - vec.append(4); - vec.set(0, 100); - - // Second iteration - vector expected = {100, 2, 3, 4}; - size_t idx = 0; - for (auto val : vec) { - assert(val == expected[idx]); - idx++; - } - assert(idx == 4); - } - - // Test 12: Iterator copy construction - { - RobustPagedVectorImpl vec; - vec.append(10); - vec.append(20); - - auto it1 = vec.begin(); - auto it2(it1); // Copy constructor - - assert(it1 == it2); - assert(*it1 == *it2); - assert(*it1 == 10); - } - - // Test 13: Iterator assignment - { - RobustPagedVectorImpl vec; - vec.append(10); - vec.append(20); - vec.append(30); - - auto it1 = vec.begin(); - auto it2 = vec.begin(); - ++it2; - - assert(*it1 == 10); - assert(*it2 == 20); - - it1 = it2; // Assignment - assert(it1 == it2); - assert(*it1 == 20); - } - - cerr << "RobustPagedVector (" << typeid(RobustPagedVectorImpl).name() << ") iterator tests successful!" << endl; -} - void test_packed_deque() { enum deque_op_t {SET = 0, GET = 1, APPEND_LEFT = 2, POP_LEFT = 3, APPEND_RIGHT = 4, POP_RIGHT = 5, SERIALIZE = 6}; std::random_device rd; @@ -3576,7 +3245,7 @@ void test_packed_deque() { case APPEND_LEFT: for (size_t k = 0; k < appends_per_op; k++) { std_deq.push_front(next_val); - suc_deq.append_front(next_val); + suc_deq.push_front(next_val); next_val++; } @@ -3593,7 +3262,7 @@ void test_packed_deque() { case APPEND_RIGHT: for (size_t k = 0; k < appends_per_op; k++) { std_deq.push_back(next_val); - suc_deq.append_back(next_val); + suc_deq.push_back(next_val); next_val++; } @@ -3633,276 +3302,6 @@ void test_packed_deque() { cerr << "PackedDeque tests successful!" << endl; } -template -void test_packed_deque_iterators() { - // Test 1: Empty deque iteration - { - PackedDequeImpl deque; - assert(deque.begin() == deque.end()); - - size_t count = 0; - for (auto it = deque.begin(); it != deque.end(); ++it) { - count++; - } - assert(count == 0); - } - - // Test 2: Single element - { - PackedDequeImpl deque; - deque.append_back(42); - - assert(deque.begin() != deque.end()); - - auto it = deque.begin(); - assert(*it == 42); - ++it; - assert(it == deque.end()); - } - - // Test 3: Multiple elements - basic iteration - { - PackedDequeImpl deque; - vector expected = {10, 20, 30, 40, 50}; - - for (auto val : expected) { - deque.append_back(val); - } - - // Iterate and compare - size_t idx = 0; - for (auto it = deque.begin(); it != deque.end(); ++it) { - assert(idx < expected.size()); - assert(*it == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 4: Range-based for loop - { - PackedDequeImpl deque; - vector expected = {100, 200, 300, 400, 500, 600, 700, 800}; - - for (auto val : expected) { - deque.append_back(val); - } - - size_t idx = 0; - for (auto val : deque) { - assert(idx < expected.size()); - assert(val == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 5: Iterator equality and inequality - { - PackedDequeImpl deque; - deque.append_back(1); - deque.append_back(2); - deque.append_back(3); - - auto it1 = deque.begin(); - auto it2 = deque.begin(); - assert(it1 == it2); - - ++it2; - assert(it1 != it2); - - ++it1; - assert(it1 == it2); - } - - // Test 6: Iteration after mixed front/back operations - { - PackedDequeImpl deque; - deque.append_back(3); - deque.append_back(4); - deque.append_front(2); - deque.append_front(1); - deque.append_back(5); - - vector expected = {1, 2, 3, 4, 5}; - size_t idx = 0; - for (auto val : deque) { - assert(val == expected[idx]); - idx++; - } - assert(idx == 5); - } - - // Test 7: Iteration with circular buffer wrap-around - { - PackedDequeImpl deque; - // Add elements to force circular buffer behavior - for (size_t i = 0; i < 20; i++) { - deque.append_back(i); - } - // Remove from front - for (size_t i = 0; i < 10; i++) { - deque.pop_front(); - } - // Add more to back - for (size_t i = 20; i < 30; i++) { - deque.append_back(i); - } - - // Should now have 10-29 - vector expected; - for (size_t i = 10; i < 30; i++) { - expected.push_back(i); - } - - size_t idx = 0; - for (auto val : deque) { - assert(val == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 8: std::distance compatibility - { - PackedDequeImpl deque; - for (size_t i = 0; i < 15; i++) { - deque.append_back(i); - } - - auto dist = std::distance(deque.begin(), deque.end()); - assert((size_t)dist == deque.size()); - assert((size_t)dist == 15); - } - - // Test 9: std::find compatibility - { - PackedDequeImpl deque; - deque.append_back(10); - deque.append_back(20); - deque.append_back(30); - deque.append_back(40); - deque.append_back(50); - - auto it = std::find(deque.begin(), deque.end(), 30); - assert(it != deque.end()); - assert(*it == 30); - - auto it2 = std::find(deque.begin(), deque.end(), 999); - assert(it2 == deque.end()); - } - - // Test 10: Const iterator - { - PackedDequeImpl deque; - deque.append_back(5); - deque.append_back(15); - deque.append_back(25); - - const PackedDequeImpl& const_deque = deque; - - size_t count = 0; - for (auto it = const_deque.begin(); it != const_deque.end(); ++it) { - count++; - } - assert(count == 3); - - auto it = const_deque.begin(); - assert(*it == 5); - ++it; - assert(*it == 15); - ++it; - assert(*it == 25); - } - - // Test 11: Large deque - { - PackedDequeImpl deque; - random_device rd; - default_random_engine prng(rd()); - uniform_int_distribution val_distr(0, 10000); - - vector expected; - size_t num_elements = 200; - - for (size_t i = 0; i < num_elements; i++) { - uint64_t val = val_distr(prng); - expected.push_back(val); - deque.append_back(val); - } - - size_t idx = 0; - for (auto val : deque) { - assert(val == expected[idx]); - idx++; - } - assert(idx == expected.size()); - } - - // Test 12: Iteration after modification - { - PackedDequeImpl deque; - deque.append_back(1); - deque.append_back(2); - deque.append_back(3); - - // First iteration - size_t count = 0; - for (auto it = deque.begin(); it != deque.end(); ++it) { - count++; - } - assert(count == 3); - - // Modify - deque.append_back(4); - deque.set(0, 100); - - // Second iteration - vector expected = {100, 2, 3, 4}; - size_t idx = 0; - for (auto val : deque) { - assert(val == expected[idx]); - idx++; - } - assert(idx == 4); - } - - // Test 13: Iterator copy construction - { - PackedDequeImpl deque; - deque.append_back(10); - deque.append_back(20); - - auto it1 = deque.begin(); - auto it2(it1); // Copy constructor - - assert(it1 == it2); - assert(*it1 == *it2); - assert(*it1 == 10); - } - - // Test 14: Iterator assignment - { - PackedDequeImpl deque; - deque.append_back(10); - deque.append_back(20); - deque.append_back(30); - - auto it1 = deque.begin(); - auto it2 = deque.begin(); - ++it2; - - assert(*it1 == 10); - assert(*it2 == 20); - - it1 = it2; // Assignment - assert(it1 == it2); - assert(*it1 == 20); - } - - cerr << "PackedDeque (" << typeid(PackedDequeImpl).name() << ") iterator tests successful!" << endl; -} - void test_packed_set() { enum set_op_t {INSERT = 0, REMOVE = 1, FIND = 2}; @@ -5528,9 +4927,9 @@ int main(void) { test_packed_vector>(); test_packed_vector>(); test_packed_vector>(); - test_packed_vector_iterators>(); - test_packed_vector_iterators>(); - test_packed_vector_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); test_paged_vector>(); test_paged_vector>(); test_paged_vector>(); @@ -5538,24 +4937,24 @@ int main(void) { test_paged_vector>(); test_paged_vector>(); test_paged_vector>(); - test_paged_vector_iterators>(); - test_paged_vector_iterators>(); - test_paged_vector_iterators>(); - test_paged_vector_iterators>(); - test_paged_vector_iterators>(); - test_paged_vector_iterators>(); - test_paged_vector_iterators>(); - test_paged_vector_iterators>(); - test_robust_paged_vector_iterators>(); - test_robust_paged_vector_iterators>(); - test_robust_paged_vector_iterators>(); - test_robust_paged_vector_iterators>(); - test_robust_paged_vector_iterators>(); - test_robust_paged_vector_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); test_packed_deque(); - test_packed_deque_iterators>(); - test_packed_deque_iterators>(); - test_packed_deque_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); test_packed_set(); test_mutable_path_handle_graphs(); test_deletable_handle_graphs(); From ea3bb0ac56beab8f0ddb14db45a12594f7dbfb4f Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 19 Nov 2025 18:02:21 -0500 Subject: [PATCH 6/9] Unify the 4 iterator types Anthropic Claude spent all my tokens generating into 1 --- bdsg/include/bdsg/internal/packed_structs.hpp | 517 +++++------------- 1 file changed, 134 insertions(+), 383 deletions(-) diff --git a/bdsg/include/bdsg/internal/packed_structs.hpp b/bdsg/include/bdsg/internal/packed_structs.hpp index dd2273cf..d0736256 100644 --- a/bdsg/include/bdsg/internal/packed_structs.hpp +++ b/bdsg/include/bdsg/internal/packed_structs.hpp @@ -26,7 +26,57 @@ using namespace std; * Repack an SDSL int vector, or any int vector that implements a repack(). */ template -inline void repack(IntVector& target, size_t new_width, size_t new_size); +inline void repack(IntVector& target, size_t new_width, size_t new_size); + + +/** + * A forward iterator for anything vector-like (PackedVector, PagedVector, + * etc.) that provides read-only access to elements and internally uses integer + * indexes. + * + * This iterator is invalidated if the backing object changes size or moves. + */ +template +class IndexingIterator { +public: + // Iterator traits for standard library compatibility + using iterator_category = std::forward_iterator_tag; + using value_type = uint64_t; + using difference_type = typename std::make_signed::type; + using pointer = void; + using reference = uint64_t; + + // Standard iterator operations + IndexingIterator(const IndexingIterator& other) = default; + IndexingIterator& operator=(const IndexingIterator& other) = default; + ~IndexingIterator() = default; + + /// Pre-increment operator + IndexingIterator& operator++(); + + /// Post-increment operator + IndexingIterator operator++(int); + + /// Dereference operator - returns value at current position + uint64_t operator*() const; + + /// Equality comparison + bool operator==(const IndexingIterator& other) const; + + /// Inequality comparison + bool operator!=(const IndexingIterator& other) const; + +private: + // Private constructor - only associated class can create iterators + IndexingIterator(const VectorLike* vec, size_t idx); + + const VectorLike* vec_ptr = nullptr; + size_t index = 0; + + // We're not allowed to use "class" when befriending a template parameter. + // See + friend VectorLike; +}; /* * A dynamic integer vector that maintains integers in bit-compressed form. @@ -38,8 +88,8 @@ class PackedVector { using IntVector = typename IntVectorFor::type; public: - /// Forward declaration of iterator - class iterator; + /// Allow iteration with a general indexing iterator. + using iterator = IndexingIterator; /// Constructor (starts empty) PackedVector(); @@ -118,62 +168,20 @@ class PackedVector { /// Clears the backing vector. inline void clear(); - - /// Reports the amount of memory consumed by this object in bytes. - size_t memory_usage() const; /// Iterator to the first element iterator begin() const; /// Iterator to the past-the-end element iterator end() const; + + /// Reports the amount of memory consumed by this object in bytes. + size_t memory_usage() const; /// Returns true if the contents are identical (but not necessarily storage /// parameters, such as pointer to data, capacity, bit width, etc.). inline bool operator==(const PackedVector& other) const; - /** - * A forward iterator for PackedVector that provides read-only access to elements - */ - class iterator { - public: - // Iterator traits for standard library compatibility - using iterator_category = std::forward_iterator_tag; - using value_type = uint64_t; - using difference_type = typename std::make_signed::type; - using pointer = void; - using reference = uint64_t; - - // Standard iterator operations - iterator(const iterator& other) = default; - iterator& operator=(const iterator& other) = default; - ~iterator() = default; - - /// Pre-increment operator - iterator& operator++(); - - /// Post-increment operator - iterator operator++(int); - - /// Dereference operator - returns value at current position - uint64_t operator*() const; - - /// Equality comparison - bool operator==(const iterator& other) const; - - /// Inequality comparison - bool operator!=(const iterator& other) const; - - private: - // Private constructor - only PackedVector can create iterators - iterator(const PackedVector* vec, size_t idx); - - const PackedVector* vec_ptr = nullptr; - size_t index = 0; - - friend class PackedVector; - }; - private: // We don't allocate ourselves, so we don't need to hold an allocator. @@ -205,8 +213,8 @@ class PagedVector { public: - /// Forward declaration of iterator - class iterator; + /// Allow iteration with a general indexing iterator. + using iterator = IndexingIterator; /// Construct (starts empty) PagedVector(); @@ -264,61 +272,19 @@ class PagedVector { /// Clears the backing vector inline void clear(); - - /// Returns the page width of the vector - inline size_t page_width() const; /// Iterator to the first element iterator begin() const; /// Iterator to the past-the-end element iterator end() const; + + /// Returns the page width of the vector + inline size_t page_width() const; /// Reports the amount of memory consumed by this object in bytes size_t memory_usage() const; - /** - * A forward iterator for PagedVector that provides read-only access to elements - */ - class iterator { - public: - // Iterator traits for standard library compatibility - using iterator_category = std::forward_iterator_tag; - using value_type = uint64_t; - using difference_type = typename std::make_signed::type; - using pointer = void; - using reference = uint64_t; - - // Standard iterator operations - iterator(const iterator& other) = default; - iterator& operator=(const iterator& other) = default; - ~iterator() = default; - - /// Pre-increment operator - iterator& operator++(); - - /// Post-increment operator - iterator operator++(int); - - /// Dereference operator - returns value at current position - uint64_t operator*() const; - - /// Equality comparison - bool operator==(const iterator& other) const; - - /// Inequality comparison - bool operator!=(const iterator& other) const; - - private: - // Private constructor - only PagedVector can create iterators - iterator(const PagedVector* vec, size_t idx); - - const PagedVector* vec_ptr = nullptr; - size_t index = 0; - - friend class PagedVector; - }; - private: inline uint64_t to_diff(const uint64_t& value, const uint64_t& page) const; @@ -348,8 +314,8 @@ class RobustPagedVector { using PagedVec = PagedVector; public: - /// Forward declaration of iterator - class iterator; + /// Allow iteration with a general indexing iterator. + using iterator = IndexingIterator; /// Construct (starts empty) RobustPagedVector(); @@ -407,61 +373,19 @@ class RobustPagedVector { /// Clears the backing vector inline void clear(); - - /// Returns the page width of the vector - inline size_t page_width() const; /// Iterator to the first element iterator begin() const; /// Iterator to the past-the-end element iterator end() const; + + /// Returns the page width of the vector + inline size_t page_width() const; /// Reports the amount of memory consumed by this object in bytes size_t memory_usage() const; - /** - * A forward iterator for RobustPagedVector that provides read-only access to elements - */ - class iterator { - public: - // Iterator traits for standard library compatibility - using iterator_category = std::forward_iterator_tag; - using value_type = uint64_t; - using difference_type = typename std::make_signed::type; - using pointer = void; - using reference = uint64_t; - - // Standard iterator operations - iterator(const iterator& other) = default; - iterator& operator=(const iterator& other) = default; - ~iterator() = default; - - /// Pre-increment operator - iterator& operator++(); - - /// Post-increment operator - iterator operator++(int); - - /// Dereference operator - returns value at current position - uint64_t operator*() const; - - /// Equality comparison - bool operator==(const iterator& other) const; - - /// Inequality comparison - bool operator!=(const iterator& other) const; - - private: - // Private constructor - only RobustPagedVector can create iterators - iterator(const RobustPagedVector* vec, size_t idx); - - const RobustPagedVector* vec_ptr = nullptr; - size_t index = 0; - - friend class RobustPagedVector; - }; - private: /// The first page_size entries go in this vector @@ -484,8 +408,8 @@ class PackedDeque { using PackedVec = PackedVector; public: - /// Forward declaration of iterator - class iterator; + /// Allow iteration with a general indexing iterator. + using iterator = IndexingIterator; /// Construct empty PackedDeque(void); @@ -551,48 +475,6 @@ class PackedDeque { /// Reports the amount of memory consumed by this object in bytes. size_t memory_usage() const; - /** - * A forward iterator for PackedDeque that provides read-only access to elements - */ - class iterator { - public: - // Iterator traits for standard library compatibility - using iterator_category = std::forward_iterator_tag; - using value_type = uint64_t; - using difference_type = typename std::make_signed::type; - using pointer = void; - using reference = uint64_t; - - // Standard iterator operations - iterator(const iterator& other) = default; - iterator& operator=(const iterator& other) = default; - ~iterator() = default; - - /// Pre-increment operator - iterator& operator++(); - - /// Post-increment operator - iterator operator++(int); - - /// Dereference operator - returns value at current position - uint64_t operator*() const; - - /// Equality comparison - bool operator==(const iterator& other) const; - - /// Inequality comparison - bool operator!=(const iterator& other) const; - - private: - // Private constructor - only PackedDeque can create iterators - iterator(const PackedDeque* deque, size_t idx); - - const PackedDeque* deque_ptr = nullptr; - size_t index = 0; - - friend class PackedDeque; - }; - private: inline void contract(); @@ -766,7 +648,44 @@ inline void repack>(sdsl::int_vector<>& target, size_t new_wi target = std::move(tmp); } - +///////////////////// +/// IndexingIterator +///////////////////// + +template +IndexingIterator::IndexingIterator(const VectorLike* vec, size_t idx) + : vec_ptr(vec), index(idx) { + // Constructor +} + +template +IndexingIterator& IndexingIterator::operator++() { + ++index; + return *this; +} + +template +IndexingIterator IndexingIterator::operator++(int) { + IndexingIterator tmp = *this; + ++index; + return tmp; +} + +template +uint64_t IndexingIterator::operator*() const { + return vec_ptr->get(index); +} + +template +bool IndexingIterator::operator==(const IndexingIterator& other) const { + return vec_ptr == other.vec_ptr && index == other.index; +} + +template +bool IndexingIterator::operator!=(const IndexingIterator& other) const { + return !(*this == other); +} + ///////////////////// /// PackedVector ///////////////////// @@ -857,6 +776,16 @@ inline void PackedVector::clear() { filled = 0; } +template +typename PackedVector::iterator PackedVector::begin() const { + return iterator(this, 0); +} + +template +typename PackedVector::iterator PackedVector::end() const { + return iterator(this, filled); +} + template inline bool PackedVector::operator==(const PackedVector& other) const { if (size() != other.size()) { @@ -920,58 +849,6 @@ size_t PackedVector::memory_usage() const { return sizeof(filled) + sizeof(vec) + vec.capacity() / 8; } -///////////////////// -/// PackedVector::iterator -///////////////////// - -template -PackedVector::iterator::iterator(const PackedVector* vec, size_t idx) - : vec_ptr(vec), index(idx) { - // Constructor -} - -template -typename PackedVector::iterator& -PackedVector::iterator::operator++() { - ++index; - return *this; -} - -template -typename PackedVector::iterator -PackedVector::iterator::operator++(int) { - iterator tmp = *this; - ++index; - return tmp; -} - -template -uint64_t PackedVector::iterator::operator*() const { - return vec_ptr->get(index); -} - -template -bool PackedVector::iterator::operator==(const iterator& other) const { - return vec_ptr == other.vec_ptr && index == other.index; -} - -template -bool PackedVector::iterator::operator!=(const iterator& other) const { - return !(*this == other); -} - -template -typename PackedVector::iterator -PackedVector::begin() const { - return iterator(this, 0); -} - -template -typename PackedVector::iterator -PackedVector::end() const { - return iterator(this, filled); -} - ///////////////////// /// PackedDeque ///////////////////// @@ -1132,55 +1009,13 @@ inline void PackedDeque::clear() { begin_idx = 0; } -///////////////////// -/// PackedDeque::iterator -///////////////////// - -template -PackedDeque::iterator::iterator(const PackedDeque* deque, size_t idx) - : deque_ptr(deque), index(idx) { - // Constructor -} - template -typename PackedDeque::iterator& -PackedDeque::iterator::operator++() { - ++index; - return *this; -} - -template -typename PackedDeque::iterator -PackedDeque::iterator::operator++(int) { - iterator tmp = *this; - ++index; - return tmp; -} - -template -uint64_t PackedDeque::iterator::operator*() const { - return deque_ptr->get(index); -} - -template -bool PackedDeque::iterator::operator==(const iterator& other) const { - return deque_ptr == other.deque_ptr && index == other.index; -} - -template -bool PackedDeque::iterator::operator!=(const iterator& other) const { - return !(*this == other); -} - -template -typename PackedDeque::iterator -PackedDeque::begin() const { +typename PackedDeque::iterator PackedDeque::begin() const { return iterator(this, 0); } template -typename PackedDeque::iterator -PackedDeque::end() const { +typename PackedDeque::iterator PackedDeque::end() const { return iterator(this, filled); } @@ -1344,6 +1179,16 @@ inline void PagedVector::clear() { filled = 0; } +template +typename PagedVector::iterator PagedVector::begin() const { + return iterator(this, 0); +} + +template +typename PagedVector::iterator PagedVector::end() const { + return iterator(this, filled); +} + template inline size_t PagedVector::page_width() const { return page_size; @@ -1387,58 +1232,6 @@ inline uint64_t PagedVector::from_diff(const uint64_t& diff, } } -///////////////////// -/// PagedVector::iterator -///////////////////// - -template -PagedVector::iterator::iterator(const PagedVector* vec, size_t idx) - : vec_ptr(vec), index(idx) { - // Constructor -} - -template -typename PagedVector::iterator& -PagedVector::iterator::operator++() { - ++index; - return *this; -} - -template -typename PagedVector::iterator -PagedVector::iterator::operator++(int) { - iterator tmp = *this; - ++index; - return tmp; -} - -template -uint64_t PagedVector::iterator::operator*() const { - return vec_ptr->get(index); -} - -template -bool PagedVector::iterator::operator==(const iterator& other) const { - return vec_ptr == other.vec_ptr && index == other.index; -} - -template -bool PagedVector::iterator::operator!=(const iterator& other) const { - return !(*this == other); -} - -template -typename PagedVector::iterator -PagedVector::begin() const { - return iterator(this, 0); -} - -template -typename PagedVector::iterator -PagedVector::end() const { - return iterator(this, filled); -} - ///////////////////// /// RobustPagedVector ///////////////////// @@ -1561,60 +1354,18 @@ inline void RobustPagedVector::clear() { } template -inline size_t RobustPagedVector::page_width() const { - return latter_pages.page_width(); -} - -///////////////////// -/// RobustPagedVector::iterator -///////////////////// - -template -RobustPagedVector::iterator::iterator(const RobustPagedVector* vec, size_t idx) - : vec_ptr(vec), index(idx) { - // Constructor -} - -template -typename RobustPagedVector::iterator& -RobustPagedVector::iterator::operator++() { - ++index; - return *this; -} - -template -typename RobustPagedVector::iterator -RobustPagedVector::iterator::operator++(int) { - iterator tmp = *this; - ++index; - return tmp; -} - -template -uint64_t RobustPagedVector::iterator::operator*() const { - return vec_ptr->get(index); -} - -template -bool RobustPagedVector::iterator::operator==(const iterator& other) const { - return vec_ptr == other.vec_ptr && index == other.index; -} - -template -bool RobustPagedVector::iterator::operator!=(const iterator& other) const { - return !(*this == other); +typename RobustPagedVector::iterator RobustPagedVector::begin() const { + return iterator(this, 0); } template -typename RobustPagedVector::iterator -RobustPagedVector::begin() const { - return iterator(this, 0); +typename RobustPagedVector::iterator RobustPagedVector::end() const { + return iterator(this, size()); } template -typename RobustPagedVector::iterator -RobustPagedVector::end() const { - return iterator(this, size()); +inline size_t RobustPagedVector::page_width() const { + return latter_pages.page_width(); } ///////////////////// From 13ea17d23e7c3ba68138351a98cf8b54d97983e0 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 19 Nov 2025 18:28:08 -0500 Subject: [PATCH 7/9] Fluff up iterator into a random access iterator with comparison --- bdsg/include/bdsg/internal/packed_structs.hpp | 120 +++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/bdsg/include/bdsg/internal/packed_structs.hpp b/bdsg/include/bdsg/internal/packed_structs.hpp index d0736256..7280b7e1 100644 --- a/bdsg/include/bdsg/internal/packed_structs.hpp +++ b/bdsg/include/bdsg/internal/packed_structs.hpp @@ -40,7 +40,7 @@ template class IndexingIterator { public: // Iterator traits for standard library compatibility - using iterator_category = std::forward_iterator_tag; + using iterator_category = std::random_access_iterator_tag; using value_type = uint64_t; using difference_type = typename std::make_signed::type; using pointer = void; @@ -66,6 +66,55 @@ class IndexingIterator { /// Inequality comparison bool operator!=(const IndexingIterator& other) const; + // Bidirectional methods + + /// Pre-decrement operator + IndexingIterator& operator--(); + + /// Post-decrement operator + IndexingIterator operator--(int); + + // Random-access methods + + /// Addition of offset in place + IndexingIterator& operator+=(difference_type offset); + + /// Addition of offset + IndexingIterator operator+(difference_type offset) const; + + /// Subtraction of offset in place + IndexingIterator& operator-=(difference_type offset); + + /// Subtraction of offset + IndexingIterator operator-(difference_type offset) const; + + /// Subtraction of two iterators + difference_type operator-(const IndexingIterator& other) const; + + /// Indexing into iterator. Even though we type this as reference, remember + /// that we don't actually implement writing to our "references" and just + /// use the value type. + /// Result is undefined if itrators are to different collecitons. + reference operator[](difference_type offset) const; + + // Comaprable iterator methods (TODO: Is there an STL concept name for this?) + + /// Determine if this iterator is strictly before another. + /// Result is undefined if iterators are to different collecitons. + bool operator<(const IndexingIterator& other) const; + + /// Determine if this iterator is before or at another. + /// Result is undefined if iterators are to different collecitons. + bool operator<=(const IndexingIterator& other) const; + + /// Determine if this iterator is strictly after another. + /// Result is undefined if iterators are to different collecitons. + bool operator>(const IndexingIterator& other) const; + + /// Determine if this iterator is at or after another. + /// Result is undefined if itrators are to different collecitons. + bool operator>=(const IndexingIterator& other) const; + private: // Private constructor - only associated class can create iterators IndexingIterator(const VectorLike* vec, size_t idx); @@ -686,6 +735,75 @@ bool IndexingIterator::operator!=(const IndexingIterator& other) con return !(*this == other); } +template +IndexingIterator& IndexingIterator::operator--() { + --index; + return *this; +} + +template +IndexingIterator IndexingIterator::operator--(int) { + IndexingIterator tmp = *this; + --index; + return tmp; +} + +template +IndexingIterator& IndexingIterator::operator+=(difference_type offset) { + index += offset; + return *this; +} + +template +IndexingIterator IndexingIterator::operator+(difference_type offset) const { + return IndexingIterator(vec_ptr, index + offset); +} + +template +IndexingIterator& IndexingIterator::operator-=(difference_type offset) { + index -= offset; + return *this; +} + +template +IndexingIterator IndexingIterator::operator-(difference_type offset) const { + return IndexingIterator(vec_ptr, index - offset); +} + +template +typename IndexingIterator::difference_type IndexingIterator::operator-(const IndexingIterator& other) const { + // TODO: I don't know a way to subtract two unsigned values and get the + // signed difference in a single operation as long as that difference + // itself fits the signed type. So we cast and hope. + return (difference_type) index - (difference_type) other.index; +} + +template +typename IndexingIterator::reference IndexingIterator::operator[](difference_type offset) const { + return *(*this + offset); +} + +template +bool IndexingIterator::operator<(const IndexingIterator& other) const { + return index < other.index; +} + +template +bool IndexingIterator::operator<=(const IndexingIterator& other) const { + return index <= other.index; +} + +template +bool IndexingIterator::operator>(const IndexingIterator& other) const { + return index > other.index; +} + +template +bool IndexingIterator::operator>=(const IndexingIterator& other) const { + return index >= other.index; +} + + ///////////////////// /// PackedVector ///////////////////// From 06227dbf2786c9c600395beb9ec211c041ca6ac3 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 19 Nov 2025 18:35:07 -0500 Subject: [PATCH 8/9] Properly deprecate old names of renamed methods --- bdsg/include/bdsg/internal/packed_structs.hpp | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/bdsg/include/bdsg/internal/packed_structs.hpp b/bdsg/include/bdsg/internal/packed_structs.hpp index 7280b7e1..de99df43 100644 --- a/bdsg/include/bdsg/internal/packed_structs.hpp +++ b/bdsg/include/bdsg/internal/packed_structs.hpp @@ -194,9 +194,21 @@ class PackedVector { /// Add a value to the end inline void push_back(const uint64_t& value); + + /// Add a value to the end (deprecated in favor of STL-like push_back) + [[deprecated("Use push_back instead of append_back")]] + inline void append_back(const uint64_t& value) { + push_back(value); + } /// Remove the last value inline void pop_back(); + + /// Remove the last value (deprecated in favor of STL-like pop_back) + [[deprecated("Use pop_back instead of pop")]] + inline void pop() { + pop_back(); + } /// Either shrink the vector or grow the vector to the new size. New /// entries created by growing are filled with 0. @@ -298,9 +310,21 @@ class PagedVector { /// Add a value to the end inline void push_back(const uint64_t& value); + + /// Add a value to the end (deprecated in favor of STL-like push_back) + [[deprecated("Use push_back instead of append_back")]] + inline void append_back(const uint64_t& value) { + push_back(value); + } /// Remove the last value inline void pop_back(); + + /// Remove the last value (deprecated in favor of STL-like pop_back) + [[deprecated("Use pop_back instead of pop")]] + inline void pop() { + pop_back(); + } /// Either shrink the vector or grow the vector to the new size. New /// entries created by growing are filled with 0. @@ -399,9 +423,21 @@ class RobustPagedVector { /// Add a value to the end inline void push_back(const uint64_t& value); + + /// Add a value to the end (deprecated in favor of STL-like push_back) + [[deprecated("Use push_back instead of append_back")]] + inline void append_back(const uint64_t& value) { + push_back(value); + } /// Remove the last value inline void pop_back(); + + /// Remove the last value (deprecated in favor of STL-like pop_back) + [[deprecated("Use pop_back instead of pop")]] + inline void pop() { + pop_back(); + } /// Either shrink the vector or grow the vector to the new size. New /// entries created by growing are filled with 0. @@ -492,9 +528,21 @@ class PackedDeque { /// Add a value to the front inline void push_front(const uint64_t& value); + + /// Add a value to the front (deprecated in favor of STL-like push_front) + [[deprecated("Use push_front instead of append_front")]] + inline void append_front(const uint64_t& value) { + push_front(value); + } /// Add a value to the back inline void push_back(const uint64_t& value); + + /// Add a value to the back (deprecated in favor of STL-like push_back) + [[deprecated("Use push_back instead of append_back")]] + inline void append_back(const uint64_t& value) { + push_back(value); + } /// Remove the front value inline void pop_front(); From b3c090e7e0399bfbb1c4e9975d5a6a07ccc1832f Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Thu, 20 Nov 2025 08:29:21 -0500 Subject: [PATCH 9/9] Add tests for the more advanced iterator features --- bdsg/src/test_libbdsg.cpp | 125 +++++++++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/bdsg/src/test_libbdsg.cpp b/bdsg/src/test_libbdsg.cpp index 9da2e186..94c936d0 100644 --- a/bdsg/src/test_libbdsg.cpp +++ b/bdsg/src/test_libbdsg.cpp @@ -2877,10 +2877,15 @@ void test_packed_vector() { /** * Generic iterator test function that works with any vector-like container - * (PackedVector, PagedVector, RobustPagedVector, PackedDeque) + * (PackedVector, PagedVector, RobustPagedVector, PackedDeque). + * + * Tests ForwardIterator, BidirectionalIterator, RandomAccessIterator, and + * iterator order comparison, but not OutputIterator. */ template void test_iterators() { + // ForwardIterator tests + // Empty iteration { VectorLike vec; @@ -3098,6 +3103,124 @@ void test_iterators() { assert(*it1 == 20); } + // BidirectionalIterator tests. + { + VectorLike vec; + vec.push_back(10); + vec.push_back(20); + vec.push_back(30); + + auto it1 = vec.begin(); + auto it2 = it1; + ++it2; + auto also_decremented = --it2; + + assert(it2 == it1); + assert(also_decremented == it1); + + it2++; + auto not_decremented = it2--; + + assert(it2 == it1); + assert(not_decremented != it1); + assert(*not_decremented == 20); + + auto it3 = vec.end(); + it3--; + assert(it3 != vec.end()); + assert(*it3 == 30); + } + + // RandomAccessIterator tests + { + VectorLike vec; + vec.push_back(10); + vec.push_back(20); + vec.push_back(30); + + auto it1 = vec.begin(); + auto it2 = it1; + + it1 += 1; + assert(*it1 == 20); + + it1 += 2; + assert(it1 == vec.end()); + + it1 -= 1; + auto it3 = it2 + 2; + assert(it1 == it3); + assert(*it1 == 30); + assert(it2 == vec.begin()); + + auto it4 = it1 - 2; + assert(*it4 == 10); + + assert(*it1 == vec.begin()[2]); + assert(*it4 == vec.begin()[0]); + assert(it4[2] == *it1); + assert(it1[-2] == *it4); + + assert(it1 + -2 == it4); + assert(it4 - -2 == it1); + + it1 += -2; + assert(it1 == it4); + + it1 -= -1; + it4++; + assert(it1 == it4); + } + + // Iterator comparison tests + { + VectorLike vec; + vec.push_back(10); + vec.push_back(20); + vec.push_back(30); + + auto it1 = vec.begin(); + auto it2 = it1; + + assert(it1 >= it2); + assert(it1 <= it2); + assert(!(it1 < it2)); + assert(!(it1 > it2)); + it1++; + + assert(it1 >= it2); + assert(!(it1 <= it2)); + assert(!(it2 >= it1)); + assert(it2 <= it1); + assert(!(it1 < it2)); + assert(it1 > it2); + assert(it2 < it1); + assert(!(it2 > it1)); + } + + // Iterator distance tests + { + VectorLike vec; + vec.push_back(10); + vec.push_back(20); + vec.push_back(30); + + assert(vec.end() - vec.begin() == vec.size()); + + auto it1 = vec.begin(); + auto it2 = it1; + + it1 += 1; + it2 += 2; + + assert(it2 - it1 == 1); + assert(it1 - it2 == -1); + + it1--; + assert(it2 - it1 == 2); + assert(it1 - it2 == -2); + } + cerr << "Iterator (" << typeid(typename VectorLike::iterator).name() << ") tests successful!" << endl; }