From b174208b5cf20c584833220ae6281118dea6439a Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 21 May 2025 23:23:12 -0500 Subject: [PATCH 01/14] Add a feature to the readme checklist --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 91de09d..8fd82fc 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,9 @@ ordered_map/ ./build.sh ``` -## Future Improvements +## Future Features + +- [ ] Add `move_to_front` and `move_to_back` operations in ordered_map to re-order entries (without affecting or copying the entry value) - [ ] Add const iterators - [ ] Add reverse iterators @@ -206,8 +208,7 @@ ordered_map/ - [ ] **DoublyLinkedList:** Add `erase` method to erase elements given their iterator - [ ] **DoublyLinkedList:** Add pre and post decrement operators - [ ] **DoublyLinkedList:** Test front(), back(), insertion and deletion functions for copying behavior -- [ ] **OrderedMap:** Add support for initializing map with +- [ ] **OrderedMap:** Add support for initializing map with - [ ] **OrderedMap:** Add support for custom hash functions - [ ] **OrderedMap:** Add `erase` method to erase elements given their iterator or key - [ ] Test for memory leaks - From 0c611fdbffeb52aaa12b4cf00f3ef72458e97c87 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Fri, 23 May 2025 21:08:33 -0500 Subject: [PATCH 02/14] Create tests for moving objects, modify linked list push_back to support moving objects --- include/doubly_linked_list.hpp | 24 +++++++++++++++----- tests/doubly_linked_list/push_back_tests.cpp | 24 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 20e4bc5..a2db888 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -9,8 +9,9 @@ struct Node { std::unique_ptr next; Node* prev; - Node(const T& a_value, Node* a_prev, std::unique_ptr a_next) - : value(a_value), prev(a_prev), next(std::move(a_next)) {} + template + Node(U&& a_value, Node* a_prev, std::unique_ptr a_next) + : value(std::forward(a_value)), prev(a_prev), next(std::move(a_next)) {} }; template @@ -45,15 +46,26 @@ class DoublyLinkedList { return tail->value; } - void push_back(const T& value) { + template + void push_back_internal(U&& value) { + auto new_node = std::make_unique>(std::forward(value), tail, nullptr); if (!head) { - head = std::make_unique>(value, nullptr, nullptr); + head = std::move(new_node); tail = head.get(); } else { - tail->next = std::make_unique>(value, tail, nullptr); + tail->next = std::move(new_node); + tail->next->prev = tail; tail = tail->next.get(); } - _size++; + ++_size; + } + + void push_back(T&& value) { + push_back_internal(std::move(value)); + } + + void push_back(const T& value) { + push_back_internal(value); } T pop_back() { diff --git a/tests/doubly_linked_list/push_back_tests.cpp b/tests/doubly_linked_list/push_back_tests.cpp index 5e7d3e7..9da2933 100644 --- a/tests/doubly_linked_list/push_back_tests.cpp +++ b/tests/doubly_linked_list/push_back_tests.cpp @@ -57,3 +57,27 @@ TEST_CASE("push_back 2, 1 and back returns 1", "[push_back]") { list.push_back(1); REQUIRE(list.back() == 1); } + +class MoveCopyFlag { + public: + std::string state; + MoveCopyFlag() : state("default") {} + MoveCopyFlag(const MoveCopyFlag& other) : state(other.state + ", copied") {} + MoveCopyFlag(MoveCopyFlag&& other) : state(other.state + ", moved") {} + }; + +TEST_CASE("using move with push_back does not copy the value", "[push_back]") { + DoublyLinkedList list; + auto object = MoveCopyFlag(); + list.push_back(std::move(object)); + + REQUIRE(list.back().state == "default, moved"); +} + +TEST_CASE("using push_back directly copies the value", "[push_back]") { + DoublyLinkedList list; + auto object = MoveCopyFlag(); + list.push_back(object); + + REQUIRE(list.back().state == "default, copied"); +} \ No newline at end of file From 5c99a354e5fd9a2dfe71dc31c59e6ef82557e684 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Fri, 23 May 2025 21:40:53 -0500 Subject: [PATCH 03/14] Refactor, add tests for moving in push_front, make them pass - Move the MoveCopyFlag class to a separate file to import in different test files (DRY) - Create tests for moving objects in push_front - Overload push_front to support moving --- include/doubly_linked_list.hpp | 57 ++++++++++++------- tests/doubly_linked_list/push_back_tests.cpp | 12 +--- tests/doubly_linked_list/push_front_tests.cpp | 17 +++++- tests/tests_utils.hpp | 10 ++++ 4 files changed, 63 insertions(+), 33 deletions(-) create mode 100644 tests/tests_utils.hpp diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index a2db888..137e095 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -21,6 +21,35 @@ class DoublyLinkedList { Node* tail; int _size; + + template + void push_back_internal(U&& value) { + auto new_node = std::make_unique>(std::forward(value), tail, nullptr); + if (!head) { + head = std::move(new_node); + tail = head.get(); + } else { + tail->next = std::move(new_node); + tail->next->prev = tail; + tail = tail->next.get(); + } + ++_size; + } + + template + void push_front_internal(U&& value) { + auto new_node = std::make_unique>(std::forward(value), nullptr, std::move(head)); + if (new_node->next) { + new_node->next->prev = new_node.get(); + } + else { + tail = new_node.get(); + } + head = std::move(new_node); + _size++; + } + + public: DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} ~DoublyLinkedList() { @@ -46,19 +75,7 @@ class DoublyLinkedList { return tail->value; } - template - void push_back_internal(U&& value) { - auto new_node = std::make_unique>(std::forward(value), tail, nullptr); - if (!head) { - head = std::move(new_node); - tail = head.get(); - } else { - tail->next = std::move(new_node); - tail->next->prev = tail; - tail = tail->next.get(); - } - ++_size; - } + void push_back(T&& value) { push_back_internal(std::move(value)); @@ -88,16 +105,12 @@ class DoublyLinkedList { return popped_value; } + void push_front(T&& value) { + push_front_internal(std::move(value)); + } + void push_front(const T& value) { - if (!head) { - head = std::make_unique>(value, nullptr, nullptr); - tail = head.get(); - } else { - auto new_node = std::make_unique>(value, nullptr, std::move(head)); - new_node->next->prev = new_node.get(); - head = std::move(new_node); - } - _size++; + push_front_internal(value); } T pop_front() { diff --git a/tests/doubly_linked_list/push_back_tests.cpp b/tests/doubly_linked_list/push_back_tests.cpp index 9da2933..686d55b 100644 --- a/tests/doubly_linked_list/push_back_tests.cpp +++ b/tests/doubly_linked_list/push_back_tests.cpp @@ -1,6 +1,6 @@ #include #include "doubly_linked_list.hpp" - +#include "../tests_utils.hpp" TEST_CASE("push_back one value in an empty list makes size = 1", "[push_back]") { DoublyLinkedList list; @@ -58,14 +58,6 @@ TEST_CASE("push_back 2, 1 and back returns 1", "[push_back]") { REQUIRE(list.back() == 1); } -class MoveCopyFlag { - public: - std::string state; - MoveCopyFlag() : state("default") {} - MoveCopyFlag(const MoveCopyFlag& other) : state(other.state + ", copied") {} - MoveCopyFlag(MoveCopyFlag&& other) : state(other.state + ", moved") {} - }; - TEST_CASE("using move with push_back does not copy the value", "[push_back]") { DoublyLinkedList list; auto object = MoveCopyFlag(); @@ -74,7 +66,7 @@ TEST_CASE("using move with push_back does not copy the value", "[push_back]") { REQUIRE(list.back().state == "default, moved"); } -TEST_CASE("using push_back directly copies the value", "[push_back]") { +TEST_CASE("passing variable by value to push_back copies the value", "[push_back]") { DoublyLinkedList list; auto object = MoveCopyFlag(); list.push_back(object); diff --git a/tests/doubly_linked_list/push_front_tests.cpp b/tests/doubly_linked_list/push_front_tests.cpp index 9903a1c..641d278 100644 --- a/tests/doubly_linked_list/push_front_tests.cpp +++ b/tests/doubly_linked_list/push_front_tests.cpp @@ -1,6 +1,6 @@ #include #include "doubly_linked_list.hpp" - +#include "../tests_utils.hpp" TEST_CASE("push_front 1, size = 1", "[push_front]") { @@ -45,3 +45,18 @@ TEST_CASE("push_back 1, push_front 2, pop_back twice, size is 0", "[push_front]" list.pop_back(); REQUIRE(list.size() == 0); } + +TEST_CASE("using move with push_front does not copy the value", "[push_front]") { + DoublyLinkedList list; + auto object = MoveCopyFlag(); + list.push_front(std::move(object)); + + REQUIRE(list.front().state == "default, moved"); +} + +TEST_CASE("passing variable by value to push_front copies the value", "[push_front]") { + DoublyLinkedList list; + auto object = MoveCopyFlag(); + list.push_front(object); + REQUIRE(list.front().state == "default, copied"); +} \ No newline at end of file diff --git a/tests/tests_utils.hpp b/tests/tests_utils.hpp new file mode 100644 index 0000000..f1730ba --- /dev/null +++ b/tests/tests_utils.hpp @@ -0,0 +1,10 @@ +#pragma once +#include + +class MoveCopyFlag { + public: + std::string state; + MoveCopyFlag() : state("default") {} + MoveCopyFlag(const MoveCopyFlag& other) : state(other.state + ", copied") {} + MoveCopyFlag(MoveCopyFlag&& other) : state(other.state + ", moved") {} +}; \ No newline at end of file From 7f2582b01d99fc1f86bc01d4082ef5c8192684b6 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Fri, 23 May 2025 22:02:55 -0500 Subject: [PATCH 04/14] Create tests for find() method in map and implement it --- CMakeLists.txt | 1 + include/ordered_map.hpp | 5 ++++- tests/ordered_map/find_tests.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/ordered_map/find_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 60396bc..94be1dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ add_executable( tests/ordered_map/lookup_tests.cpp tests/ordered_map/iterator_tests.cpp tests/ordered_map/constructor_tests.cpp + tests/ordered_map/find_tests.cpp ) target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index 5765020..e476eb0 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -113,6 +113,9 @@ class OrderedMap { } return Iterator(_list.back_iterator()); } - + + Iterator find(const KeyType& key) { + return _map.find(key) == _map.end() ? end() : Iterator(_map[key]); + } }; \ No newline at end of file diff --git a/tests/ordered_map/find_tests.cpp b/tests/ordered_map/find_tests.cpp new file mode 100644 index 0000000..690fbce --- /dev/null +++ b/tests/ordered_map/find_tests.cpp @@ -0,0 +1,24 @@ +#include "ordered_map.hpp" +#include + +TEST_CASE("calling find on an empty map returns end()", "[find]") { + OrderedMap o_map; + REQUIRE(o_map.find(1) == o_map.end()); +} + +TEST_CASE("calling find with a key that does not exist returns end()", "[find]") { + OrderedMap o_map; + o_map.insert(1, 2); + REQUIRE(o_map.find(2) == o_map.end()); +} + +TEST_CASE("calling find with a key that exists returns an iterator to the key", "[find]") { + OrderedMap o_map; + o_map.insert(1, 2); + auto it = o_map.find(1); + + REQUIRE(*it == std::make_pair(1, 2)); +} + + + From fa286a5ad71431bb448e7628495255daf44c3250 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Fri, 23 May 2025 23:11:22 -0500 Subject: [PATCH 05/14] Add tests for erase list iterator and implement erase function --- CMakeLists.txt | 1 + include/doubly_linked_list.hpp | 22 +++++ tests/doubly_linked_list/erase_tests.cpp | 103 +++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 tests/doubly_linked_list/erase_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 94be1dc..930bcf1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable( tests/doubly_linked_list/push_back_tests.cpp tests/doubly_linked_list/push_front_tests.cpp tests/doubly_linked_list/clear_tests.cpp + tests/doubly_linked_list/erase_tests.cpp tests/ordered_map/size_tests.cpp tests/ordered_map/insertion_tests.cpp tests/ordered_map/lookup_tests.cpp diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 137e095..9b4d937 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -49,6 +49,13 @@ class DoublyLinkedList { _size++; } + T erase_middle_node(Node* node) { + T value = node->value; + node->next->prev = node->prev; + node->prev->next = std::move(node->next); + _size--; + return value; + } public: DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} @@ -192,4 +199,19 @@ class DoublyLinkedList { Iterator back_iterator() { return Iterator(tail); } + + + Iterator erase(Iterator it) { + if (empty()) throw std::out_of_range("List is empty"); + if (it == end()) throw std::out_of_range("Invalid iterator"); + + Iterator target_it = it++; + + target_it == begin() ? pop_front() : + target_it == back_iterator() ? pop_back() : + erase_middle_node(target_it.current_node_ptr); + + return it; + } + }; diff --git a/tests/doubly_linked_list/erase_tests.cpp b/tests/doubly_linked_list/erase_tests.cpp new file mode 100644 index 0000000..dd0900b --- /dev/null +++ b/tests/doubly_linked_list/erase_tests.cpp @@ -0,0 +1,103 @@ +#include "doubly_linked_list.hpp" +#include + +TEST_CASE("calling erase on an empty list throws an exception", "[erase]") { + DoublyLinkedList list; + REQUIRE_THROWS_AS(list.erase(list.begin()), std::out_of_range); +} + +TEST_CASE("push_back 1, call erase on end throws an exception", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE_THROWS_AS(list.erase(list.end()), std::out_of_range); +} + +TEST_CASE("push_back 1, call erase on begin returns end iterator", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.erase(list.begin()) == list.end()); +} + +TEST_CASE("push_back 1, call erase on begin makes the list empty", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.erase(list.begin()); + REQUIRE(list.empty()); +} + +TEST_CASE("push_back 1, 2, call erase on begin makes the front equal 2", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.erase(list.begin()); + REQUIRE(list.front() == 2); +} + +TEST_CASE("push_back 1, 2, call erase on back_iterator makes back equal 1", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.erase(list.back_iterator()); + REQUIRE(list.back() == 1); +} + +TEST_CASE("push_back 1, 2, call erase on begin returns iterator to 2", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + auto it = list.erase(list.begin()); + REQUIRE(*it == 2); +} + +TEST_CASE("push_back 1, 2, 3, call erase on second element makes list size = 2", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + auto it = list.begin(); list.erase(++it); + + REQUIRE(list.size() == 2); +} + +TEST_CASE("push_back 1, 2, 3, call erase on second element front = 1, back = 3", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + auto it = list.begin(); list.erase(++it); + + REQUIRE(list.front() == 1); + REQUIRE(list.back() == 3); +} + +TEST_CASE("push_back 10,000 elements, erase all elements", "[erase]") { + DoublyLinkedList list; + for (int i = 0; i < 10000; i++) { + list.push_back(i); + } + + auto it = list.begin(); + while (it != list.end()) { + it = list.erase(it); + } + + REQUIRE(list.empty()); +} + +TEST_CASE("push_back 10,000 elements, erase every other element", "[erase]") { + DoublyLinkedList list; + for (int i = 0; i < 10000; i++) { + list.push_back(i); + } + + auto it = list.begin(); + while (it != list.end()) { + it = list.erase(it); + if (it != list.end()) { + it++; + } + } + + REQUIRE(list.size() == 5000); +} + From 4c275b0c338f8a79e11fce957a58cbc165f6c301 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Fri, 23 May 2025 23:23:15 -0500 Subject: [PATCH 06/14] Add test for using -- on end() iterator in list, make it pass --- include/doubly_linked_list.hpp | 16 ++++++++++------ tests/doubly_linked_list/iterator_tests.cpp | 8 ++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 9b4d937..b729c5c 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -147,13 +147,14 @@ class DoublyLinkedList { class Iterator { Node* current_node_ptr; + DoublyLinkedList* list_ptr; - Iterator(Node* a_current) : current_node_ptr(a_current) {} + Iterator(Node* a_current, DoublyLinkedList* a_list_ptr) : current_node_ptr(a_current), list_ptr(a_list_ptr) {} friend class DoublyLinkedList; public: - Iterator() : current_node_ptr(nullptr) {} + Iterator() : current_node_ptr(nullptr), list_ptr(nullptr) {} bool operator==(const Iterator& other) const { return current_node_ptr == other.current_node_ptr; @@ -181,7 +182,10 @@ class DoublyLinkedList { } Iterator& operator--() { - if (current_node_ptr) { + if (current_node_ptr == nullptr) { + current_node_ptr = list_ptr->tail; + } + else if (current_node_ptr) { current_node_ptr = current_node_ptr->prev; } return *this; @@ -189,15 +193,15 @@ class DoublyLinkedList { }; Iterator begin() { - return Iterator(head.get()); + return Iterator(head.get(), this); } Iterator end() { - return Iterator(nullptr); + return Iterator(nullptr, this); } Iterator back_iterator() { - return Iterator(tail); + return Iterator(tail, this); } diff --git a/tests/doubly_linked_list/iterator_tests.cpp b/tests/doubly_linked_list/iterator_tests.cpp index 5b3bc82..1606831 100644 --- a/tests/doubly_linked_list/iterator_tests.cpp +++ b/tests/doubly_linked_list/iterator_tests.cpp @@ -169,3 +169,11 @@ TEST_CASE("push_back 1, 2, dereferencing back iterator returns 2", "[back_iterat REQUIRE(*it == 2); } +TEST_CASE("push_back 1, 2, 3, using decerement (--) on end iterator returns 3", "[back_iterator]") { + DoublyLinkedList list; + list.push_back(1); list.push_back(2); list.push_back(3); + auto it = list.end(); + --it; + + REQUIRE(*it == 3); +} \ No newline at end of file From 623a029d2e94873d920b4cdf59c32eafd98ac1a6 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Fri, 23 May 2025 23:39:46 -0500 Subject: [PATCH 07/14] Create a test for move_to_front function in ordered map, make it pass --- CMakeLists.txt | 1 + include/ordered_map.hpp | 7 ++++++ tests/ordered_map/move_to_front_tests.cpp | 26 +++++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 tests/ordered_map/move_to_front_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 930bcf1..bdc031f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ add_executable( tests/ordered_map/iterator_tests.cpp tests/ordered_map/constructor_tests.cpp tests/ordered_map/find_tests.cpp + tests/ordered_map/move_to_front_tests.cpp ) target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index e476eb0..f97cde5 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -118,4 +118,11 @@ class OrderedMap { return _map.find(key) == _map.end() ? end() : Iterator(_map[key]); } + void move_to_front(const KeyType& key) { + if (_map.find(key) == _map.end()) { + throw std::out_of_range("Key not found in ordered map"); + } + _list.push_front(std::make_pair(key, std::move(at(key)))); + _list.erase(_map[key]); + } }; \ No newline at end of file diff --git a/tests/ordered_map/move_to_front_tests.cpp b/tests/ordered_map/move_to_front_tests.cpp new file mode 100644 index 0000000..eb2111e --- /dev/null +++ b/tests/ordered_map/move_to_front_tests.cpp @@ -0,0 +1,26 @@ +#include +#include "ordered_map.hpp" +#include "../tests_utils.hpp" + +TEST_CASE("move_to_front on an empty map throws an exception", "[move_to_front]") { + OrderedMap map; + REQUIRE_THROWS_AS(map.move_to_front(1), std::out_of_range); +} + +TEST_CASE("move_to_front on a map with one element has the same element at the front", "[move_to_front]") { + OrderedMap map; + map.insert(1, 0); + map.move_to_front(1); + REQUIRE(map.begin()->first == 1); +} + +TEST_CASE("move_to_front on a map with two elements moves the element to the front", "[move_to_front]") { + OrderedMap map; + map.insert(1, 0); + map.insert(2, 0); + map.move_to_front(2); + REQUIRE(map.begin()->first == 2); +} + + + From b55de192264be215b0ebd781ddaaa06059c8c85f Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sat, 24 May 2025 11:18:26 -0500 Subject: [PATCH 08/14] Create tests for move_to_begin method in list, implement the method --- CMakeLists.txt | 1 + include/doubly_linked_list.hpp | 13 +++++ .../move_to_begin_tests.cpp | 47 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 tests/doubly_linked_list/move_to_begin_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bdc031f..262a746 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable( tests/doubly_linked_list/push_front_tests.cpp tests/doubly_linked_list/clear_tests.cpp tests/doubly_linked_list/erase_tests.cpp + tests/doubly_linked_list/move_to_begin_tests.cpp tests/ordered_map/size_tests.cpp tests/ordered_map/insertion_tests.cpp tests/ordered_map/lookup_tests.cpp diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index b729c5c..aca7ade 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -218,4 +218,17 @@ class DoublyLinkedList { return it; } + void move_to_begin(Iterator it) { + if (empty()) throw std::out_of_range("List is empty"); + if (it == end()) throw std::out_of_range("Invalid iterator"); + if (it == begin()) return; + + if (it == back_iterator()) { + push_front(pop_back()); + } + else { + push_front(erase_middle_node(it.current_node_ptr)); + } + } + }; diff --git a/tests/doubly_linked_list/move_to_begin_tests.cpp b/tests/doubly_linked_list/move_to_begin_tests.cpp new file mode 100644 index 0000000..9fb7cce --- /dev/null +++ b/tests/doubly_linked_list/move_to_begin_tests.cpp @@ -0,0 +1,47 @@ +#include +#include "doubly_linked_list.hpp" +#include "../tests_utils.hpp" + +TEST_CASE("move_to_front on an empty list throws an exception", "[move_to_front]") { + DoublyLinkedList list; + REQUIRE_THROWS_AS(list.move_to_begin(list.begin()), std::out_of_range); +} + +TEST_CASE("push_back 1, 2,move_to_front with end iterator throws an exception", "[move_to_front]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE_THROWS_AS(list.move_to_begin(list.end()), std::out_of_range); +} + +TEST_CASE("push_back 1, 2, move_to_front with begin iterator does nothing", "[move_to_front]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.move_to_begin(list.begin()); + REQUIRE(list.front() == 1); + REQUIRE(list.back() == 2); +} + +TEST_CASE("push_back 1, 2, move_to_front with iterator to 2 moves 2 to front", "[move_to_front]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + auto it = list.end(); + list.move_to_begin(--it); + REQUIRE(list.front() == 2); + REQUIRE(list.back() == 1); +} + +TEST_CASE("push_back 1, 2, 3, move_to_begin with iterator to 2 moves 2 to front", "[move_to_begin]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + auto it = list.begin(); + list.move_to_begin(++it); + + REQUIRE(list.front() == 2); + REQUIRE(*++it == 1); + REQUIRE(list.back() == 3); +} \ No newline at end of file From e314eaa54e789ee6c9c7a8e9decb6b737c9042c9 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sat, 24 May 2025 11:35:18 -0500 Subject: [PATCH 09/14] Create constructor tests for linkedlist and implement constructors - Constructor with initalizer list - Constructor with begin and end iterators --- CMakeLists.txt | 1 + include/doubly_linked_list.hpp | 12 ++++ .../doubly_linked_list/constructor_tests.cpp | 59 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/doubly_linked_list/constructor_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 262a746..22c85bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable( tests/doubly_linked_list/pop_front_tests.cpp tests/doubly_linked_list/push_back_tests.cpp tests/doubly_linked_list/push_front_tests.cpp + tests/doubly_linked_list/constructor_tests.cpp tests/doubly_linked_list/clear_tests.cpp tests/doubly_linked_list/erase_tests.cpp tests/doubly_linked_list/move_to_begin_tests.cpp diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index aca7ade..90983ca 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -59,6 +59,18 @@ class DoublyLinkedList { public: DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} + + DoublyLinkedList(std::initializer_list init_list) : DoublyLinkedList() { + for (const auto& value : init_list) push_back(value); + } + + template + DoublyLinkedList(ParamIterator begin, ParamIterator end) : DoublyLinkedList() { + for (auto it = begin; it != end; it++) { + push_back(*it); + } + } + ~DoublyLinkedList() { clear(); } diff --git a/tests/doubly_linked_list/constructor_tests.cpp b/tests/doubly_linked_list/constructor_tests.cpp new file mode 100644 index 0000000..aa49fa2 --- /dev/null +++ b/tests/doubly_linked_list/constructor_tests.cpp @@ -0,0 +1,59 @@ +#include +#include "doubly_linked_list.hpp" +#include "../tests_utils.hpp" +#include +TEST_CASE("Default constructor creates an empty list", "[constructor]") { + DoublyLinkedList list; + REQUIRE(list.empty()); +} + +TEST_CASE("Constructor with initializer list {1, 2} creates a list with the elements in the initializer list", "[constructor]") { + DoublyLinkedList list = {1, 2}; + REQUIRE(list.size() == 2); + REQUIRE(list.front() == 1); + REQUIRE(list.back() == 2); +} + +TEST_CASE("Constructor with initializer list {1, 2, 3, 4, 5} creates a list with the elements in the initializer list", "[constructor]") { + DoublyLinkedList list = {1, 2, 3, 4, 5}; + int value = 1; + for (auto it = list.begin(); it != list.end(); ++it) { + REQUIRE(*it == value++); + } +} + +TEST_CASE("Constructor with vector of size 10 creates a list with 10 elements", "[constructor]") { + std::vector vec(10); + DoublyLinkedList list(vec.begin(), vec.end()); + REQUIRE(list.size() == 10); +} + +TEST_CASE("Constructor with vector {1, 2, 3, 4, 5} creates a list with the elements in the vector", "[constructor]") { + std::vector vec = {1, 2, 3, 4, 5}; + DoublyLinkedList list(vec.begin(), vec.end()); + + auto vec_it = vec.begin(); + auto list_it = list.begin(); + + while (vec_it != vec.end()) { + REQUIRE(*list_it == *vec_it); + ++vec_it; ++list_it; + } + REQUIRE(list_it == list.end()); + REQUIRE(vec_it == vec.end()); +} + +TEST_CASE("Constructor with set of strings creates a list with the elements in the set", "[constructor]") { + std::set set = {"hello", "world", "test"}; + DoublyLinkedList list(set.begin(), set.end()); + + auto set_it = set.begin(); + auto list_it = list.begin(); + + while (set_it != set.end()) { + REQUIRE(*list_it == *set_it); + ++set_it; ++list_it; + } + REQUIRE(list_it == list.end()); + REQUIRE(set_it == set.end()); +} From cfbf1fde032727d8725411b6561c47736ac4414a Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sat, 24 May 2025 11:36:16 -0500 Subject: [PATCH 10/14] Simplify move_to_begin tests --- .../move_to_begin_tests.cpp | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/tests/doubly_linked_list/move_to_begin_tests.cpp b/tests/doubly_linked_list/move_to_begin_tests.cpp index 9fb7cce..e3a0a77 100644 --- a/tests/doubly_linked_list/move_to_begin_tests.cpp +++ b/tests/doubly_linked_list/move_to_begin_tests.cpp @@ -7,41 +7,32 @@ TEST_CASE("move_to_front on an empty list throws an exception", "[move_to_front] REQUIRE_THROWS_AS(list.move_to_begin(list.begin()), std::out_of_range); } -TEST_CASE("push_back 1, 2,move_to_front with end iterator throws an exception", "[move_to_front]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); +TEST_CASE("list with 1, 2, move_to_front with end iterator throws an exception", "[move_to_front]") { + DoublyLinkedList list = {1, 2}; REQUIRE_THROWS_AS(list.move_to_begin(list.end()), std::out_of_range); } -TEST_CASE("push_back 1, 2, move_to_front with begin iterator does nothing", "[move_to_front]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); +TEST_CASE("list with 1, 2, move_to_front with begin iterator does nothing", "[move_to_front]") { + DoublyLinkedList list = {1, 2}; list.move_to_begin(list.begin()); REQUIRE(list.front() == 1); REQUIRE(list.back() == 2); } -TEST_CASE("push_back 1, 2, move_to_front with iterator to 2 moves 2 to front", "[move_to_front]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); +TEST_CASE("list with 1, 2, move_to_front with iterator to 2 moves 2 to front", "[move_to_front]") { + DoublyLinkedList list = {1, 2}; auto it = list.end(); list.move_to_begin(--it); REQUIRE(list.front() == 2); REQUIRE(list.back() == 1); } -TEST_CASE("push_back 1, 2, 3, move_to_begin with iterator to 2 moves 2 to front", "[move_to_begin]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - list.push_back(3); +TEST_CASE("list with 1, 2, 3, move_to_begin with iterator to 2 moves 2 to front", "[move_to_begin]") { + DoublyLinkedList list = {1, 2, 3}; auto it = list.begin(); list.move_to_begin(++it); REQUIRE(list.front() == 2); REQUIRE(*++it == 1); REQUIRE(list.back() == 3); -} \ No newline at end of file +} From 4de01b894fc7f4b5d8da1385bc36fa1c940f3f1b Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sat, 24 May 2025 14:29:56 -0500 Subject: [PATCH 11/14] Add tests for moving and copying objects, modify list to pass the tests - Create helper function extract_node_and_link_prev_with_next to extract node without affecting value or iterator - Create helper function emplace_node_before to insert node at any desired position without affecting value or iterator - Modify pop_back(), pop_front() and erase_middle_node() to move the values insead of copying them --- include/doubly_linked_list.hpp | 53 +++++++++++++++---- tests/doubly_linked_list/front_and_back.cpp | 22 ++++++++ .../move_to_begin_tests.cpp | 50 +++++++++++++++++ 3 files changed, 116 insertions(+), 9 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 90983ca..425d79f 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -50,13 +50,52 @@ class DoublyLinkedList { } T erase_middle_node(Node* node) { - T value = node->value; + T value = std::move(node->value); node->next->prev = node->prev; node->prev->next = std::move(node->next); _size--; return value; } + std::unique_ptr> extract_node_and_link_prev_with_next(Node* node) { + std::unique_ptr> extracted; + + if (node->next) { + node->next->prev = node->prev; + } else { + tail = node->prev; + } + + if (node->prev) { + extracted = std::move(node->prev->next); + node->prev->next = std::move(node->next); + } else { + extracted = std::move(head); + head = std::move(node->next); + } + + return extracted; + } + + void emplace_node_before(std::unique_ptr> node, Node* position) { + if (position == head.get()) { + node->next = std::move(head); + if (node->next) node->next->prev = node.get(); + head = std::move(node); + } + else if (position == nullptr) { + node->prev = tail; + tail = node.get(); + node->prev->next = std::move(node); + } + else { + node->prev = position->prev; + node->next = std::move(position->prev->next); + position->prev = node.get(); + node->prev->next = std::move(node); + } + } + public: DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} @@ -109,7 +148,7 @@ class DoublyLinkedList { throw std::out_of_range("List is empty"); } - T popped_value = tail->value; + T popped_value = std::move(tail->value); if (tail == head.get()) { head.reset(); @@ -137,7 +176,7 @@ class DoublyLinkedList { throw std::out_of_range("List is empty"); } - T popped_value = head->value; + T popped_value = std::move(head->value); head = std::move(head->next); if (!head) @@ -235,12 +274,8 @@ class DoublyLinkedList { if (it == end()) throw std::out_of_range("Invalid iterator"); if (it == begin()) return; - if (it == back_iterator()) { - push_front(pop_back()); - } - else { - push_front(erase_middle_node(it.current_node_ptr)); - } + auto extracted = extract_node_and_link_prev_with_next(it.current_node_ptr); + emplace_node_before(std::move(extracted), head.get()); } }; diff --git a/tests/doubly_linked_list/front_and_back.cpp b/tests/doubly_linked_list/front_and_back.cpp index c245bd5..3c9b6f4 100644 --- a/tests/doubly_linked_list/front_and_back.cpp +++ b/tests/doubly_linked_list/front_and_back.cpp @@ -1,6 +1,7 @@ #include #include "doubly_linked_list.hpp" #include +#include "../tests_utils.hpp" TEST_CASE("Canary test", "[canary]") { REQUIRE(true); @@ -11,6 +12,27 @@ TEST_CASE("front on an empty list throws an exception", "[front]") { REQUIRE_THROWS_AS(list.front(), std::out_of_range); } +TEST_CASE("front on a list with one element returns the element", "[front]") { + DoublyLinkedList list = {1}; + REQUIRE(list.front() == 1); +} + +TEST_CASE("front on a list with {1, 2} returns 1", "[front]") { + DoublyLinkedList list = {1, 2}; + REQUIRE(list.front() == 1); +} + +TEST_CASE("front on a list with a MoveCopyFlag element returns the element without copying it", "[front]") { + DoublyLinkedList list; + list.push_back(MoveCopyFlag()); + REQUIRE(list.front().state == "default, moved"); +} + +TEST_CASE("front on a list constructed with initializer list of MoveCopyFlag returns the element copied once", "[front]") { + DoublyLinkedList list = {MoveCopyFlag()}; + REQUIRE(list.front().state == "default, copied"); +} + TEST_CASE("back on an empty list throws an exception", "[back]") { DoublyLinkedList list; REQUIRE_THROWS_AS(list.back(), std::out_of_range); diff --git a/tests/doubly_linked_list/move_to_begin_tests.cpp b/tests/doubly_linked_list/move_to_begin_tests.cpp index e3a0a77..e5e8778 100644 --- a/tests/doubly_linked_list/move_to_begin_tests.cpp +++ b/tests/doubly_linked_list/move_to_begin_tests.cpp @@ -36,3 +36,53 @@ TEST_CASE("list with 1, 2, 3, move_to_begin with iterator to 2 moves 2 to front" REQUIRE(*++it == 1); REQUIRE(list.back() == 3); } + +TEST_CASE("list with 2 MoveCopyFlag elements, move_to_begin with iterator to 1st does nothing, no additional move or copy", "[move_to_begin]") { + DoublyLinkedList list; + list.push_back(MoveCopyFlag()); + list.push_back(MoveCopyFlag()); + auto it = list.begin(); + list.move_to_begin(it); + REQUIRE(list.front().state == "default, moved"); + REQUIRE(list.back().state == "default, moved"); +} + +TEST_CASE("list with 2 MoveCopyFlag elements constructed with move, move_to_begin with iterator to 2nd moves it to front with no additional move or copy", "[move_to_begin]") { + DoublyLinkedList list; + list.push_back(MoveCopyFlag()); + list.push_back(MoveCopyFlag()); + auto it = list.begin(); + auto it_to_second_element = ++it; + list.move_to_begin(it_to_second_element); + + REQUIRE(list.front().state == "default, moved"); +} + +TEST_CASE("list with 5 MoveCopyFlag elements copied via initializer list, move_to_begin with iterator to 3rd moves it to front with no additional move or copy", "[move_to_begin]") { + DoublyLinkedList list = { + MoveCopyFlag(), + MoveCopyFlag(), + MoveCopyFlag(), + MoveCopyFlag(), + MoveCopyFlag() + }; + auto it = list.begin(); ++it; ++it; + list.move_to_begin(it); + REQUIRE(list.front().state == "default, copied"); +} + +TEST_CASE("list with 5 strings, reversed using move_to_begin", "[move_to_begin]") { + DoublyLinkedList list = {"a", "b", "c", "d", "e"}; + auto it = list.begin(); + while(it != list.end()) { + list.move_to_begin(it++); + } + + it = list.begin(); + REQUIRE(*it++ == "e"); + REQUIRE(*it++ == "d"); + REQUIRE(*it++ == "c"); + REQUIRE(*it++ == "b"); + REQUIRE(*it++ == "a"); +} + From 281290f9585ae636a7d0e9dfe4aa0cfb508f0464 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sat, 24 May 2025 15:12:26 -0500 Subject: [PATCH 12/14] Create tests for list.emplace_back and implement it to make them pass --- CMakeLists.txt | 1 + include/doubly_linked_list.hpp | 23 ++++++++++++++++++- .../doubly_linked_list/emplace_back_tests.cpp | 15 ++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/doubly_linked_list/emplace_back_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 22c85bc..735dfac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable( tests/doubly_linked_list/clear_tests.cpp tests/doubly_linked_list/erase_tests.cpp tests/doubly_linked_list/move_to_begin_tests.cpp + tests/doubly_linked_list/emplace_back_tests.cpp tests/ordered_map/size_tests.cpp tests/ordered_map/insertion_tests.cpp tests/ordered_map/lookup_tests.cpp diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 425d79f..8c06f9e 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -12,6 +12,11 @@ struct Node { template Node(U&& a_value, Node* a_prev, std::unique_ptr a_next) : value(std::forward(a_value)), prev(a_prev), next(std::move(a_next)) {} + + template + Node(Node* p, std::unique_ptr n, Args&&... args) + : value(std::forward(args)...), prev(p), next(std::move(n)) {} + }; template @@ -187,7 +192,7 @@ class DoublyLinkedList { } void clear() { - while(size() > 0) { + while(head) { pop_back(); } } @@ -278,4 +283,20 @@ class DoublyLinkedList { emplace_node_before(std::move(extracted), head.get()); } + template + void emplace_back(Args&&... args) { + auto new_node = std::make_unique>(tail, nullptr, std::forward(args)...); + + if (!head) { + head = std::move(new_node); + tail = head.get(); + } else { + tail->next = std::move(new_node); + tail->next->prev = tail; + tail = tail->next.get(); + } + ++_size; + } + + }; diff --git a/tests/doubly_linked_list/emplace_back_tests.cpp b/tests/doubly_linked_list/emplace_back_tests.cpp new file mode 100644 index 0000000..0e40ffb --- /dev/null +++ b/tests/doubly_linked_list/emplace_back_tests.cpp @@ -0,0 +1,15 @@ +#include +#include "doubly_linked_list.hpp" +#include "../tests_utils.hpp" + +TEST_CASE("emplace_back one value in an empty list makes size = 1", "[emplace_back]") { + DoublyLinkedList list; + list.emplace_back(1); + REQUIRE(list.size() == 1); +} + +TEST_CASE("emplace_back one pair in an empty list makes size = 1", "[emplace_back]") { + DoublyLinkedList> list; + list.emplace_back(1, 2); + REQUIRE(list.size() == 1); +} From f1c3b6e2a061d794839de812b12f8f062cc915a2 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sat, 24 May 2025 15:15:39 -0500 Subject: [PATCH 13/14] Refactor: extract shared logic in push_back_internal and emplace_back in link_new_back_node function --- include/doubly_linked_list.hpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 8c06f9e..44fb04d 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -26,10 +26,7 @@ class DoublyLinkedList { Node* tail; int _size; - - template - void push_back_internal(U&& value) { - auto new_node = std::make_unique>(std::forward(value), tail, nullptr); + void link_new_back_node(std::unique_ptr> new_node) { if (!head) { head = std::move(new_node); tail = head.get(); @@ -41,6 +38,12 @@ class DoublyLinkedList { ++_size; } + template + void push_back_internal(U&& value) { + auto new_node = std::make_unique>(std::forward(value), tail, nullptr); + link_new_back_node(std::move(new_node)); + } + template void push_front_internal(U&& value) { auto new_node = std::make_unique>(std::forward(value), nullptr, std::move(head)); @@ -286,16 +289,7 @@ class DoublyLinkedList { template void emplace_back(Args&&... args) { auto new_node = std::make_unique>(tail, nullptr, std::forward(args)...); - - if (!head) { - head = std::move(new_node); - tail = head.get(); - } else { - tail->next = std::move(new_node); - tail->next->prev = tail; - tail = tail->next.get(); - } - ++_size; + link_new_back_node(std::move(new_node)); } From 7ab74c5291f0366f918c080a5f84c18e5c8bb75a Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sat, 24 May 2025 15:26:57 -0500 Subject: [PATCH 14/14] Create tests for move_to_front and make them pass - Define (=) operator for the MoveCopyFlag to fix the deleted operator error - Overload insert to support moving without copying - Use emplace_back instead of push_back to avoid unnecessary moves or copies - Use move_to_begin list function instead of push_front with erase --- include/ordered_map.hpp | 17 ++++++-- tests/ordered_map/move_to_front_tests.cpp | 53 ++++++++++++++++++----- tests/tests_utils.hpp | 10 +++++ 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index f97cde5..cb6927f 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -33,11 +33,21 @@ class OrderedMap { void insert(const KeyType& key, const ValueType& value) { if (_map.find(key) == _map.end()) { - _list.push_back(std::make_pair(key, value)); + _list.emplace_back(key, value); _map[key] = _list.back_iterator(); } else { - *_map.find(key)->second = std::make_pair(key, value); + (*_map[key]).second = value; + } + } + + void insert(const KeyType& key, ValueType&& value) { + if (_map.find(key) == _map.end()) { + _list.emplace_back(key, std::move(value)); + _map[key] = _list.back_iterator(); + } + else { + (*_map[key]).second = std::move(value); } } @@ -122,7 +132,6 @@ class OrderedMap { if (_map.find(key) == _map.end()) { throw std::out_of_range("Key not found in ordered map"); } - _list.push_front(std::make_pair(key, std::move(at(key)))); - _list.erase(_map[key]); + _list.move_to_begin(_map[key]); } }; \ No newline at end of file diff --git a/tests/ordered_map/move_to_front_tests.cpp b/tests/ordered_map/move_to_front_tests.cpp index eb2111e..e363c10 100644 --- a/tests/ordered_map/move_to_front_tests.cpp +++ b/tests/ordered_map/move_to_front_tests.cpp @@ -3,24 +3,55 @@ #include "../tests_utils.hpp" TEST_CASE("move_to_front on an empty map throws an exception", "[move_to_front]") { - OrderedMap map; - REQUIRE_THROWS_AS(map.move_to_front(1), std::out_of_range); + OrderedMap o_map; + REQUIRE_THROWS_AS(o_map.move_to_front(1), std::out_of_range); } TEST_CASE("move_to_front on a map with one element has the same element at the front", "[move_to_front]") { - OrderedMap map; - map.insert(1, 0); - map.move_to_front(1); - REQUIRE(map.begin()->first == 1); + OrderedMap o_map; + o_map.insert(1, 0); + o_map.move_to_front(1); + REQUIRE(o_map.begin()->first == 1); } TEST_CASE("move_to_front on a map with two elements moves the element to the front", "[move_to_front]") { - OrderedMap map; - map.insert(1, 0); - map.insert(2, 0); - map.move_to_front(2); - REQUIRE(map.begin()->first == 2); + OrderedMap o_map; + o_map.insert(1, 0); + o_map.insert(2, 0); + o_map.move_to_front(2); + + REQUIRE(o_map.begin()->first == 2); } +TEST_CASE("move_to_front does not copy the value", "[move_to_front]") { + OrderedMap o_map; + o_map.insert(1, MoveCopyFlag()); + o_map.insert(2, MoveCopyFlag()); + o_map.move_to_front(2); + + REQUIRE(o_map.begin()->second.state == "default, moved"); +} + +TEST_CASE("map with 5 strings, move_to_front reverses the order of the elements") { + OrderedMap o_map = { + {"a", 0}, + {"b", 0}, + {"c", 0}, + {"d", 0}, + {"e", 0} + }; + + auto it = o_map.begin(); + while (it != o_map.end()) { + o_map.move_to_front((it++)->first); + } + + it = o_map.begin(); + REQUIRE((it++)->first == "e"); + REQUIRE((it++)->first == "d"); + REQUIRE((it++)->first == "c"); + REQUIRE((it++)->first == "b"); + REQUIRE((it++)->first == "a"); +} diff --git a/tests/tests_utils.hpp b/tests/tests_utils.hpp index f1730ba..7146f6e 100644 --- a/tests/tests_utils.hpp +++ b/tests/tests_utils.hpp @@ -7,4 +7,14 @@ class MoveCopyFlag { MoveCopyFlag() : state("default") {} MoveCopyFlag(const MoveCopyFlag& other) : state(other.state + ", copied") {} MoveCopyFlag(MoveCopyFlag&& other) : state(other.state + ", moved") {} + + MoveCopyFlag& operator=(const MoveCopyFlag& other) { + state = other.state + ", copied"; + return *this; + } + + MoveCopyFlag& operator=(MoveCopyFlag&& other) { + state = other.state + ", moved"; + return *this; + } }; \ No newline at end of file