Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,19 @@ 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/move_to_end_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
tests/ordered_map/iterator_tests.cpp
tests/ordered_map/constructor_tests.cpp
tests/ordered_map/destructor_tests.cpp
tests/ordered_map/find_tests.cpp
tests/ordered_map/clear_tests.cpp
tests/ordered_map/erase_tests.cpp
tests/ordered_map/move_to_front_tests.cpp
tests/ordered_map/move_to_back_tests.cpp
)
target_link_libraries(tests PRIVATE ordered_map_lib Catch2::Catch2WithMain)

Expand Down
238 changes: 134 additions & 104 deletions include/doubly_linked_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,89 +21,6 @@ struct Node {

template <typename T>
class DoublyLinkedList {
private:
std::unique_ptr<Node<T>> head;
Node<T>* tail;
int _size;

void link_new_back_node(std::unique_ptr<Node<T>> new_node) {
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<typename U>
void push_back_internal(U&& value) {
auto new_node = std::make_unique<Node<T>>(tail, nullptr, std::forward<U>(value));
link_new_back_node(std::move(new_node));
}

template<typename U>
void push_front_internal(U&& value) {
auto new_node = std::make_unique<Node<T>>(nullptr, std::move(head), std::forward<U>(value));
if (new_node->next) {
new_node->next->prev = new_node.get();
}
else {
tail = new_node.get();
}
head = std::move(new_node);
_size++;
}

T erase_middle_node(Node<T>* node) {
T value = std::move(node->value);
node->next->prev = node->prev;
node->prev->next = std::move(node->next);
_size--;
return value;
}

std::unique_ptr<Node<T>> extract_node_and_link_prev_with_next(Node<T>* node) {
std::unique_ptr<Node<T>> 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<T>> node, Node<T>* 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) {}

Expand Down Expand Up @@ -152,9 +69,7 @@ class DoublyLinkedList {
}

T pop_back() {
if (!head) {
throw std::out_of_range("List is empty");
}
raise_exception_if_empty();

T popped_value = std::move(tail->value);

Expand All @@ -180,9 +95,7 @@ class DoublyLinkedList {
}

T pop_front() {
if (!head) {
throw std::out_of_range("List is empty");
}
raise_exception_if_empty();

T popped_value = std::move(head->value);
head = std::move(head->next);
Expand All @@ -195,23 +108,14 @@ class DoublyLinkedList {
}

void clear() {
while(head) {
pop_back();
}
while(!empty()) pop_back();
}

bool empty() const {
return _size == 0;
return !head;
}

class Iterator {
Node<T>* current_node_ptr;
DoublyLinkedList<T>* list_ptr;

Iterator(Node<T>* a_current, DoublyLinkedList<T>* a_list_ptr) : current_node_ptr(a_current), list_ptr(a_list_ptr) {}

friend class DoublyLinkedList<T>;

public:
Iterator() : current_node_ptr(nullptr), list_ptr(nullptr) {}

Expand All @@ -224,10 +128,20 @@ class DoublyLinkedList {
}

T& operator*() const {
raise_exception_if_empty();

if (*this == list_ptr->end())
throw std::out_of_range("end iterator unexpectedly dereferenced");

return current_node_ptr->value;
}

Iterator& operator++() {
raise_exception_if_empty();

if (*this == list_ptr->end())
throw std::out_of_range("called ++ on end iterator");

if (current_node_ptr) {
current_node_ptr = current_node_ptr->next.get();
}
Expand All @@ -241,6 +155,11 @@ class DoublyLinkedList {
}

Iterator& operator--() {
raise_exception_if_empty();

if (*this == list_ptr->begin())
throw std::out_of_range("called -- on begin iterator");

if (current_node_ptr == nullptr) {
current_node_ptr = list_ptr->tail;
}
Expand All @@ -249,6 +168,23 @@ class DoublyLinkedList {
}
return *this;
}

Iterator operator--(int) {
Iterator initial_state_copy = *this;
--(*this);
return initial_state_copy;
}

private:
void raise_exception_if_empty() const {
if (list_ptr->empty())
throw std::out_of_range("Container is empty");
}

Node<T>* current_node_ptr;
DoublyLinkedList<T>* list_ptr;
Iterator(Node<T>* a_current, DoublyLinkedList<T>* a_list_ptr) : current_node_ptr(a_current), list_ptr(a_list_ptr) {}
friend class DoublyLinkedList<T>;
};

Iterator begin() {
Expand All @@ -265,8 +201,8 @@ class DoublyLinkedList {


Iterator erase(Iterator it) {
if (empty()) throw std::out_of_range("List is empty");
if (it == end()) throw std::out_of_range("Invalid iterator");
raise_exception_if_empty();
if (it == end()) throw std::out_of_range("erase called on end iterator");

Iterator target_it = it++;

Expand All @@ -278,19 +214,113 @@ class DoublyLinkedList {
}

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");
raise_exception_if_empty();
if (it == end()) throw std::out_of_range("move_to_begin called on end iterator");
if (it == begin()) return;

auto extracted = extract_node_and_link_prev_with_next(it.current_node_ptr);
emplace_node_before(std::move(extracted), head.get());
}

void move_to_end(Iterator it) {
raise_exception_if_empty();
if (it == end()) throw std::out_of_range("move_to_end called on end iterator");

auto extracted = extract_node_and_link_prev_with_next(it.current_node_ptr);
emplace_node_before(std::move(extracted), nullptr);
}

template <typename... Args>
void emplace_back(Args&&... args) {
auto new_node = std::make_unique<Node<T>>(tail, nullptr, std::forward<Args>(args)...);
link_new_back_node(std::move(new_node));
}

private:
void link_new_back_node(std::unique_ptr<Node<T>> new_node) {
if (empty()) {
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<typename U>
void push_back_internal(U&& value) {
auto new_node = std::make_unique<Node<T>>(tail, nullptr, std::forward<U>(value));
link_new_back_node(std::move(new_node));
}

template<typename U>
void push_front_internal(U&& value) {
auto new_node = std::make_unique<Node<T>>(nullptr, std::move(head), std::forward<U>(value));
if (new_node->next) {
new_node->next->prev = new_node.get();
}
else {
tail = new_node.get();
}
head = std::move(new_node);
_size++;
}

T erase_middle_node(Node<T>* node) {
T value = std::move(node->value);
node->next->prev = node->prev;
node->prev->next = std::move(node->next);
_size--;
return value;
}

std::unique_ptr<Node<T>> extract_node_and_link_prev_with_next(Node<T>* node) {
std::unique_ptr<Node<T>> 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<T>> node, Node<T>* 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);
}
}

void raise_exception_if_empty() const {
if (empty())
throw std::out_of_range("Container is empty");
}

std::unique_ptr<Node<T>> head;
Node<T>* tail;
int _size;
};
Loading