From 9a066ce16f666fe20a5f374c593e3e14b66b6da1 Mon Sep 17 00:00:00 2001 From: Pagwin Date: Sat, 23 Nov 2024 19:25:26 -0500 Subject: [PATCH] most of red-black insert done debugging it atm --- Map.hpp | 146 ++++++++-- Map.hpp.old | 768 ---------------------------------------------------- 2 files changed, 123 insertions(+), 791 deletions(-) delete mode 100644 Map.hpp.old diff --git a/Map.hpp b/Map.hpp index 78cd5a8..6f9a943 100644 --- a/Map.hpp +++ b/Map.hpp @@ -35,23 +35,32 @@ template class Map { Color color; Node *prev; Node *next; - Node(internal_ValueType val) + Map *map; + Node(internal_ValueType val, Map *map) : parent{nullptr}, val{val}, left{}, right{}, color{Color::Red}, - prev{nullptr}, next{nullptr} {} + prev{nullptr}, next{nullptr}, map{map} {} Node(const Node &rhs) : parent{nullptr}, val{rhs.val}, left{std::make_unique(*rhs.left)}, right{std::make_unique(*rhs.right)}, color{rhs.color}, - prev{nullptr}, next{nullptr} { - this->left->parent = this; - this->right->parent = this; + prev{nullptr}, next{nullptr}, map{rhs.map} { + if (this->left) { + this->left->parent = this; + } + if (this->right) { + this->right->parent = this; + } } Node(Node &&rhs) : parent{nullptr}, val{std::move(rhs.val)}, left{std::move(rhs.left)}, right{std::move(rhs.right)}, color{rhs.color}, prev{nullptr}, - next{nullptr} { - this->left->parent = this; - this->right->parent = this; + next{nullptr}, map{rhs.map} { + if (this->left) { + this->left->parent = this; + } + if (this->right) { + this->right->parent = this; + } } Node &operator=(const Node &rhs) { // retain parent as is, common case is the copy or move is happening due @@ -61,10 +70,16 @@ template class Map { this->left = std::make_unique(*rhs.left); this->right = std::make_unique(*rhs.right); - this->left->parent = this; - this->right->parent = this; - this->left->restore_ordering(); - this->right->restore_ordering(); + if (this->left) { + this->left->parent = this; + this->left->restore_ordering(); + } + if (this->right) { + this->right->parent = this; + this->right->restore_ordering(); + } + + this->map = rhs.map; return *this; } Node &operator=(Node &&rhs) { @@ -74,10 +89,15 @@ template class Map { this->val = rhs.val; this->left = std::move(rhs.left); this->right = std::move(rhs.right); - this->left->parent = this; - this->right->parent = this; - this->left->restore_ordering(); - this->right->restore_ordering(); + if (this->left) { + this->left->parent = this; + this->left->restore_ordering(); + } + if (this->right) { + this->right->parent = this; + this->right->restore_ordering(); + } + this->map = rhs.map; return *this; } Node *child(Direction dir) { @@ -150,8 +170,12 @@ template class Map { void restore_ordering() { this->prev = this->calc_pred(); this->next = this->calc_succ(); - this->prev->next = this; - this->next->prev = this; + if (this->prev) { + this->prev->next = this; + } + if (this->next) { + this->next->prev = this; + } } Node *calc_pred() { if (this->left) { @@ -216,7 +240,67 @@ template class Map { this->parent->child(!dir)->set_child(dir, std::move(self)); } // TODO: - void restore_red_black_insert() {} + // Referencing + // https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Notes_to_the_insert_diagrams + void restore_red_black_insert(Direction dir) { + Node *self = this; + + // infinite loop for case 2's sake, if tail recursion optimization was + // guaranteed I'd use tail recursion + while (true) { + Node *parent = self->parent; + // we're root, no-op (case 3) + if (!parent) { + return; + } + + // if this is violated it's a bug + assert(parent->child(dir) == self); + + // parent is black so no violation no-op (case 1) + if (parent->color == Color::Black) { + return; + } + + Node *grandparent = parent->parent; + + // parent is root (case 4) + if (!grandparent) { + parent->color = Color::Black; + return; + } + + // table showing transforms on wikipedia doesn't have this so if it + // happens it's probably a bug + assert(grandparent->color == Color::Black); + + Node *uncle = grandparent->child(!grandparent->which_child(parent)); + if (uncle == nullptr || uncle->color == Color::Black) { + if (parent->which_child(self) != grandparent->which_child(parent)) { + // we're an inner child + // case 5 + parent->rotate(dir); + self = parent; + parent = self->parent; + } + // case 6 + if (grandparent->parent == nullptr) { + map->rotate_root(!dir); + } else { + grandparent->rotate(!dir); + } + parent->color = Color::Black; + grandparent->color = Color::Red; + return; + } + + // case 2 (by process of elimination) + parent->color = Color::Black; + uncle->color = Color::Black; + grandparent->color = Color::Red; + self = grandparent; + } + } // TODO: void restore_red_black_erase() {} }; @@ -228,6 +312,7 @@ template class Map { Node *max; public: + friend Node; // public type definitions class Iterator { Node *underlying; @@ -255,6 +340,21 @@ public: std::size_t size() { return this->_size; } private: + // private helpers + void rotate_root(Direction dir) { + assert(root.has_value()); + + std::unique_ptr new_root = root.value().uchild(!dir); + // can't make null the new root + assert(new_root); + + std::unique_ptr old_root = + std::make_unique(std::move(root.value())); + + root.value() = std::move(*new_root); + + root.value().set_child(dir, std::move(old_root)); + } template std::pair locate(const Key_T &key) { Node *ret_parent; @@ -349,21 +449,21 @@ public: if (this->root.has_value()) { return std::make_pair(Iterator{&root.value()}, false); } else { - this->root = Node{val}; + this->root = Node{val, this}; return std::make_pair(Iterator{&root.value()}, true); } } // non-root node - if (!parent->child(dir)) { + if (parent->child(dir)) { // node already present return std::make_pair(Iterator{parent->child(dir)}, false); } // need to insert non-root node Node *new_node = - parent->set_child(dir, std::make_unique(Node{val})).get(); - new_node->restore_red_black_insert(); + parent->set_child(dir, std::make_unique(Node{val, this})).get(); + new_node->restore_red_black_insert(dir); new_node->restore_ordering(); return std::make_pair(Iterator{new_node}, true); } diff --git a/Map.hpp.old b/Map.hpp.old deleted file mode 100644 index 1f87b01..0000000 --- a/Map.hpp.old +++ /dev/null @@ -1,768 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -// everything is super interconnected so some forward declarations are needed at -// various points -namespace cs440 { - -template class Map; - -namespace { -enum class Color { Red, Black }; -enum class Direction { Left, Right }; -Direction operator!(Direction dir) { - switch (dir) { - case Direction::Left: - return Direction::Right; - case Direction::Right: - return Direction::Left; - default: - // unreachable the only directions are left and right - assert(false); - } -} -template struct BookKeeping { - using Self = BookKeeping; - using ValueType = std::pair; - using Ptr = typename std::vector::iterator; - friend class Map; - Map &container; - ValueType value; - // Ptr self; - Color color; - // nullptr indicates empty - std::optional parent; - std::optional left; - std::optional right; - std::optional prev; - std::optional next; - BookKeeping(Map &container) : container{container} {} - BookKeeping(BookKeeping const &rhs) - : container{rhs.container}, value{rhs.value}, // self{rhs.self}, - color{rhs.color}, parent{rhs.parent}, left{rhs.left}, right{rhs.right}, - prev{rhs.prev}, next{rhs.next} {} - // if pointing to different containers throws - BookKeeping &operator=(BookKeeping const &rhs) { - if (&this->container != &rhs.container) { - throw std::invalid_argument{"can only reassign Bookkeeping " - "values/iterators from the same map object"}; - } - this->value = rhs.value; - // this->self = rhs.self; - this->color = rhs.color; - this->parent = rhs.parent; - this->left = rhs.left; - this->right = rhs.right; - this->prev = rhs.prev; - this->next = rhs.next; - return *this; - } - // reference to a pointer because the alternatives were worse - inline Self *child(Direction dir) { - auto ret = c_select(dir); - return ret.has_value() ? &container.nodes[ret.value()] : nullptr; - } - inline std::optional c_select(Direction dir) { - switch (dir) { - case Direction::Left: - return left; - break; - case Direction::Right: - return right; - break; - default: - assert(false); - } - } - inline void c_trans(Direction dir, Self *v) { - switch (dir) { - case Direction::Left: - this->set_l(v); - break; - case Direction::Right: - this->set_r(v); - break; - default: - assert(false); - } - } - - inline Self *n() { - return next.has_value() ? &container.nodes[next.value()] : nullptr; - } - inline void set_n(Self *ptr) { - this->next = ptr == nullptr - ? std::nullopt - : std::optional{static_cast( - ptr - &this->container.nodes[0])}; - } - inline Self *p() { - return prev.has_value() ? &container.nodes[prev.value()] : nullptr; - } - inline void set_p(Self *ptr) { - this->prev = ptr == nullptr - ? std::nullopt - : std::optional{static_cast( - ptr - &this->container.nodes[0])}; - } - inline Self *r() { - return right.has_value() ? &container.nodes[right.value()] : nullptr; - } - inline void set_r(Self *ptr) { - this->right = ptr == nullptr - ? std::nullopt - : std::optional{static_cast( - ptr - &this->container.nodes[0])}; - } - inline Self *l() { - return left.has_value() ? &container.nodes[left.value()] : nullptr; - } - inline void set_l(Self *ptr) { - this->left = ptr == nullptr - ? std::nullopt - : std::optional{static_cast( - ptr - &this->container.nodes[0])}; - } - inline Self *par() { - return parent.has_value() ? &container.nodes[parent.value()] : nullptr; - } - inline void set_par(Self *ptr) { - this->parent = ptr == nullptr - ? std::nullopt - : std::optional{static_cast( - ptr - &this->container.nodes[0])}; - } - // this is root/P for this method - // copying from wikipedia RotateDirRoot with translation into my own idioms - // https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Operations - inline void rotate(Direction dir) { - // wikipedia version uses alphabet soup, might fix later - Self *P = this; - auto &T = container; - Self *G = P->par(); - Self *S = P->child(!dir); - Self *C; - - // this tidbit is wrong and wikipedia is wrong to have this assert it seems - // this method shouldn't be called in cases where this assert will trip - // assert(S != nullptr); - - C = S->child(dir); - P->c_trans(!dir, C); - - if (C != nullptr) { - C->set_par(P); - } - S->c_trans(dir, P); - P->set_par(S); - S->set_par(G); - if (G != nullptr) { - if (P == G->r()) { - G->set_r(S); - } else { - G->set_l(S); - } - } else { - T.root = S - &T.nodes[0]; - } - } -}; -} // namespace -// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree -template class Map { -private: - using ValueType = std::pair; - using Node = BookKeeping; - using Map_T = Map; - -public: - class Iterator; - class ConstIterator; - class ReverseIterator; - - friend class Iterator; - friend class ConstIterator; - friend class ReverseIterator; - friend Node; - class Iterator { - friend Map_T; - friend Node; - - private: - using Ref_T = std::optional; - Map &parent; - Ref_T ref; - Ref_T escape; - Iterator(Map &parent, Ref_T ref, - Ref_T escape = std::nullopt) - : parent{parent}, ref{ref}, escape{escape} {} - - public: - Iterator() = delete; - Iterator &operator++() { - if (ref == std::nullopt) { - ref = escape; - return *this; - } - if (parent.nodes[ref.value()].next == std::nullopt) { - escape = ref; - } - ref = parent.nodes[ref.value()].next; - return *this; - } - Iterator operator++(int) { - Iterator tmp = *this; - ++(*this); - return tmp; - } - Iterator &operator--() { - if (ref == std::nullopt) { - ref = escape; - return *this; - } - if (parent.nodes[ref.value()].prev == std::nullopt) { - escape = ref; - } - ref = parent.nodes[ref.value()].prev; - return *this; - } - Iterator operator--(int) { - Iterator tmp = *this; - --(*this); - return tmp; - } - ValueType &operator*() const { - return this->parent.nodes[this->ref.value()].value; - } - ValueType *operator->() const { return &this->operator*(); } - friend bool operator==(Iterator const &lhs, Iterator const &rhs) { - return lhs.ref == rhs.ref; - } - friend bool operator!=(Iterator const &lhs, Iterator const &rhs) { - return lhs.ref != rhs.ref; - } - friend bool operator==(ConstIterator const &lhs, Iterator const &rhs) { - return lhs.store_iter.ref == rhs.ref; - } - friend bool operator!=(ConstIterator const &lhs, Iterator const &rhs) { - return lhs.store_iter.ref != rhs.ref; - } - friend bool operator==(Iterator const &lhs, ConstIterator const &rhs) { - return lhs.ref == rhs.store_iter.ref; - } - friend bool operator!=(Iterator const &lhs, ConstIterator const &rhs) { - return lhs.ref != rhs.store_iter.ref; - } - }; - class ConstIterator { - public: - friend class Map; - friend class Iterator; - using underlying = Iterator; - - private: - underlying store_iter; - ConstIterator(underlying iter) : store_iter{iter} {} - - public: - ConstIterator() = delete; - friend bool operator==(ConstIterator const &lhs, ConstIterator const &rhs) { - return lhs.store_iter == rhs.store_iter; - } - ConstIterator &operator++() { - ++this->store_iter; - return *this; - } - ConstIterator operator++(int) { - ConstIterator tmp = *this; - this->store_iter++; - return tmp; - } - ConstIterator &operator--() { - --this->store_iter; - return *this; - } - ConstIterator operator--(int) { - ConstIterator tmp = *this; - this->store_iter--; - return tmp; - } - const ValueType &operator*() const { return *this->store_iter; } - const ValueType *operator->() const { - return this->store_iter.operator->(); - } - friend bool operator!=(ConstIterator const &lhs, ConstIterator const &rhs) { - return lhs.store_iter != rhs.store_iter; - } - }; - class ReverseIterator { - public: - friend class Map; - friend class Iterator; - using underlying = Iterator; - - private: - underlying store_iter; - - public: - ReverseIterator() = delete; - ReverseIterator(underlying store_iter) : store_iter{store_iter} {} - ReverseIterator &operator++() { - --store_iter; - return *this; - } - ReverseIterator operator++(int) { - ReverseIterator ret = *this; - ++(*this); - return ret; - } - ReverseIterator &operator--() { - ++store_iter; - return *this; - } - ReverseIterator operator--(int) { - ReverseIterator ret = *this; - --(*this); - return ret; - } - ValueType &operator*() const { return this->store_iter.ref->value; } - ValueType *operator->() const { return &this->store_iter.ref->value; } - friend bool operator==(ReverseIterator const &lhs, - ReverseIterator const &rhs) { - return lhs.store_iter == rhs.store_iter; - } - friend bool operator!=(ConstIterator const &lhs, ConstIterator const &rhs) { - return lhs.store_iter != rhs.store_iter; - } - }; - -private: - std::optional root; - std::optional min; - std::optional max; - std::vector nodes; - std::size_t size_diff; - - std::optional pred(std::size_t node) { - if (this->nodes[node].left.has_value()) { - std::size_t store = this->nodes[node].left.value(); - while (this->nodes[store].right.has_value()) { - store = this->nodes[node].right.value(); - } - return store; - } else { - if (!this->nodes[node].parent.has_value()) { - return std::nullopt; - } - - std::size_t prev_store = node; - std::size_t store = this->nodes[node].parent.value(); - while (this->nodes[store].parent.has_value()) { - if (this->nodes[store].right == prev_store) { - return store; - } - prev_store = store; - store = this->nodes[store].parent.value(); - } - return std::nullopt; - } - } - std::optional succ(std::size_t node) { - if (this->nodes[node].right.has_value()) { - std::size_t store = this->nodes[node].right.value(); - while (this->nodes[store].left.has_value()) { - store = this->nodes[node].left.value(); - } - return store; - } else { - if (!this->nodes[node].parent.has_value()) { - return std::nullopt; - } - - std::size_t prev_store = node; - std::size_t store = this->nodes[prev_store].parent.value(); - while (this->nodes[store].parent.has_value()) { - if (this->nodes[store].left == prev_store) { - return store; - } - prev_store = store; - store = this->nodes[store].parent.value(); - } - return std::nullopt; - } - } - -public: - Map() - : root{std::nullopt}, min{std::nullopt}, max{std::nullopt}, nodes{}, - size_diff{0} {} - Map(const Map &rhs) - : root{rhs.root}, min{rhs.min}, max{rhs.max}, nodes{rhs.nodes}, - size_diff{0} {} - Map &operator=(const Map &rhs) { - this->root = rhs.root; - this->min = rhs.min; - this->max = rhs.max; - this->nodes = rhs.nodes; - } - Map(std::initializer_list elems) - : root{std::nullopt}, min{std::nullopt}, max{std::nullopt}, nodes{} { - this->insert(elems.begin(), elems.end()); - } - - ~Map() {} - - size_t size() const { return this->nodes.size() - this->size_diff; } - bool empty() const { return this->size() == 0; } - - Iterator begin() { return Iterator{*this, min}; } - Iterator end() { return Iterator{*this, std::nullopt, max}; } - ConstIterator begin() const { return ConstIterator{*this, this->begin()}; } - ConstIterator end() const { return ConstIterator{*this, this->end()}; } - ConstIterator cbegin() const { return this->begin(); } - ConstIterator cend() const { return this->end(); } - ReverseIterator rbegin() { - return ReverseIterator{Iterator{*this, this->max}}; - } - ReverseIterator rend() { - return ReverseIterator{Iterator{*this, nullptr, min}}; - } - Iterator find(const Key_T &key) { - // we need a locate slot function for insert regardless so might as well use - // it here - auto [parent, dir] = this->locate_slot(key); - if (!parent.has_value()) { - if (this->root.has_value() && - this->nodes[this->root.value()].value.first == key) { - return Iterator{*this, root}; - } else { - return this->end(); - } - } - if (!this->nodes[parent.value()].c_select(dir).has_value()) { - return this->end(); - } - return Iterator{*this, this->nodes[parent.value()].c_select(dir)}; - } - // implicit cast to ConstIterator from Iterator - ConstIterator find(const Key_T &key) const { return this->find(key); } - - Mapped_T &at(const Key_T &key) { - auto ret = this->find(key); - if (ret == this->end()) { - throw std::out_of_range{"Key not in map"}; - } - return ret->second; - } - const Mapped_T &at(const Key_T &key) const { - auto ret = this->find(key); - if (ret == this->end()) { - throw std::out_of_range{"Key not in map"}; - } - return ret->second; - } - Mapped_T &operator[](const Key_T &key) { - Mapped_T v; - auto insert_val = std::make_pair(key, v); - auto [iter, key_no_exist] = this->insert(insert_val); - return iter->second; - } - -private: - void handle_root_rotation(std::optional g, - std::optional p, - std::optional i, Direction dir) { - handle_root_rotation(g.has_value() ? &this->nodes[g.value()] : nullptr, - p.has_value() ? &this->nodes[p.value()] : nullptr, - i.has_value() ? &this->nodes[i.value()] : nullptr, - dir); - } - void handle_root_rotation(Node *grandparent, Node *parent, Node *inserting, - Direction dir) { - // making inner grandchild into outer grandchild - - if (inserting == parent->child(!dir)) { - parent->rotate(dir); - inserting = parent; - parent = grandparent->child(dir); - } - grandparent->rotate(!dir); - - parent->color = Color::Black; - grandparent->color = Color::Red; - } - using Ref_T = std::optional; - // heavily referencing the wikipedia implementation for this - // https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion - void insert_helper(Ref_T to_insert, Ref_T parent, Direction dir) { - // initialize the element we're inserting - this->nodes[to_insert.value()].color = Color::Red; - this->nodes[to_insert.value()].left = std::nullopt; - this->nodes[to_insert.value()].right = std::nullopt; - this->nodes[to_insert.value()].next = std::nullopt; - this->nodes[to_insert.value()].prev = std::nullopt; - this->nodes[to_insert.value()].parent = parent; - - // if this is the first element to be inserted it's root - if (!this->nodes[to_insert.value()].parent.has_value()) { - this->root = to_insert; - this->nodes[to_insert.value()].color = Color::Black; - return; - } - - switch (dir) { - case Direction::Left: - this->nodes[parent.value()].left = to_insert; - break; - case Direction::Right: - this->nodes[parent.value()].right = to_insert; - break; - } - - do { - // don't need to keep track of these in between loops they get - // recalculated - std::optional grandparent; - std::optional uncle; - if (this->nodes[parent.value()].color == Color::Black) { - // black parent means invariants definitely hold - return; - } - - grandparent = this->nodes[parent.value()].parent; - - if (!grandparent.has_value()) { - // parent is root, just need to recolor it to black - this->nodes[parent.value()].color = Color::Black; - return; - } - - Direction parent_direction; - if (this->nodes[grandparent.value()].left == parent) { - parent_direction = Direction::Left; - uncle = this->nodes[grandparent.value()].right; - } else { - parent_direction = Direction::Right; - uncle = this->nodes[grandparent.value()].left; - } - - if (!uncle.has_value() || - this->nodes[uncle.value()].color == Color::Black) { - if (to_insert == this->nodes[parent.value()].c_select(!dir)) { - // case 5 - this->nodes[parent.value()].rotate(dir); - to_insert = parent; - parent = this->nodes[grandparent.value()].c_select(dir); - } - // case 6 - this->handle_root_rotation(grandparent, parent, to_insert, - parent_direction); - return; - } - - // now we know parent and uncle are both red so red-black coloring can be - // pushed down from grandparent - this->nodes[parent.value()].color = Color::Black; - this->nodes[uncle.value()].color = Color::Black; - this->nodes[grandparent.value()].color = Color::Red; - - to_insert = grandparent; - parent = this->nodes[to_insert.value()].parent; - } while (parent.has_value()); - - // case 3: current node is red root so we're done - } - // returns nullptr iff map is empty - std::pair, Direction> - locate_slot(const Key_T &key) { - using Ref_T = std::optional; - Ref_T current = this->root; - Ref_T parent = std::nullopt; - Direction dir; - while (current.has_value() && - this->nodes[current.value()].value.first != key) { - parent = current; - if (key < this->nodes[current.value()].value.first) { - dir = Direction::Left; - current = this->nodes[current.value()].left; - } else { - dir = Direction::Right; - current = this->nodes[current.value()].right; - } - } - return std::make_pair(parent, dir); - } - -public: - // If the key does not already exist in the map, it returns an iterator - // pointing to the new element, and true. If the key already exists, no - // insertion is performed nor is the mapped object changed, and it returns - // an iterator pointing to the element with the same key, and false. - std::pair insert(const ValueType &val) { - auto [parent, dir] = locate_slot(val.first); - bool ret = !parent.has_value() || - !this->nodes[parent.value()].c_select(dir).has_value(); - if (!ret) { - return std::make_pair( - Iterator{*this, this->nodes[parent.value()].c_select(dir)}, ret); - } - Node to_insert{*this}; - to_insert.value = val; - this->nodes.push_back(std::move(to_insert)); - // this->nodes.back().self = (--this->nodes.end()); - insert_helper(nodes.size() - 1, parent, dir); - - if (min == std::nullopt || - val.first < this->nodes[min.value()].value.first) { - min = nodes.size() - 1; - } - if (max == std::nullopt || - val.first > this->nodes[max.value()].value.first) { - max = nodes.size() - 1; - } - Ref_T successor = this->succ(this->nodes.size() - 1); - Ref_T predessor = this->pred(this->nodes.size() - 1); - if (successor.has_value()) { - this->nodes[successor.value()].prev = this->nodes.size() - 1; - } - this->nodes[this->nodes.size() - 1].next = successor; - if (predessor.has_value()) { - this->nodes[predessor.value()].next = this->nodes.size() - 1; - } - this->nodes[this->nodes.size() - 1].prev = successor; - return std::make_pair(Iterator(*this, nodes.size() - 1), ret); - } - template void insert(IT_T range_beg, IT_T range_end) { - std::for_each(range_beg, range_end, - [&](ValueType &val) { this->insert(val); }); - } - -private: - void case5(Node *parent, Node *sibling, Node *close_nephew, - Node *distant_nephew, Direction dir) { - sibling->rotate(!dir); - sibling->color = Color::Red; - close_nephew->color = Color::Black; - distant_nephew = sibling; - sibling = close_nephew; - case6(parent, sibling, distant_nephew, dir); - } - void case6(Node *parent, Node *sibling, Node *distant_nephew, Direction dir) { - parent->rotate(dir); - sibling->color = parent->color; - parent->color = Color::Black; - distant_nephew->color = Color::Black; - } - // black leaf node with no kids - void complex_erase(Iterator pos) {} - -public: - void erase(Iterator pos) { - this->size_diff++; - // simple cases - Node *ref = &this->nodes[pos.ref.value()]; - if (ref == - (this->min.has_value() ? &this->nodes[this->min.value()] : nullptr)) { - this->min = ref->next; - } - if (ref == - (this->max.has_value() ? &this->nodes[this->max.value()] : nullptr)) { - this->max = ref->prev; - } - // 2 children - if (ref->l() != nullptr && ref->r() != nullptr) { - Ref_T next = ref->next; - *ref = this->nodes[next.value()]; - this->erase(Iterator{*this, next}); - } - // single child which is left - else if (ref->l() != nullptr && ref->r() == nullptr) { - if (ref->par()->l() == ref) { - ref->par()->set_l(ref->l()); - } else { - ref->par()->set_r(ref->l()); - } - if (ref->color == Color::Black) { - ref->l()->color = Color::Black; - } - } - // single child which is right - else if (ref->l() == nullptr && ref->r() != nullptr) { - if (ref->par()->l() == ref) { - ref->par()->set_l(ref->r()); - } else { - ref->par()->set_r(ref->r()); - } - if (ref->color == Color::Black) { - ref->r()->color = Color::Black; - } - } - // no children and root - else if (this->root == pos.ref && ref->l() == nullptr && - ref->r() == nullptr) { - this->root = std::nullopt; - } - // no children and red - else if (ref->color == Color::Red && ref->l() == nullptr && - ref->r() == nullptr) { - if (ref->parent.has_value()) { - if (this->nodes[ref->parent.value()].right == pos.ref.value()) { - this->nodes[ref->parent.value()].right = std::nullopt; - } else { - this->nodes[ref->parent.value()].left = std::nullopt; - } - } - } - // complicated case of black node with no kids - else { - this->complex_erase(pos); - } - if (ref->next.has_value()) { - this->nodes[ref->next.value()].prev = this->pred(ref->next.value()); - } - if (ref->prev.has_value()) { - this->nodes[ref->next.value()].next = this->succ(ref->next.value()); - } - } - void erase(const Key_T &key) { this->erase(this->find(key)); } - void clear() { - this->root = std::nullopt; - this->nodes.clear(); - } - friend bool operator==(const Map &lhs, const Map &rhs) { - if (lhs.nodes.size() != rhs.nodes.size()) { - return false; - } - auto liter = lhs.cbegin(); - auto riter = rhs.cbegin(); - // both must be the same length so this is fine - while (liter != lhs.cend()) { - if (*liter != *riter) { - return false; - } - liter++; - riter++; - } - return true; - } - friend bool operator!=(const Map &lhs, const Map &rhs) { - return !(lhs == rhs); - } - friend bool operator<(const Map &lhs, const Map &rhs) { - auto l_iter = lhs.cbegin(); - auto r_iter = rhs.cbegin(); - for (; l_iter != lhs.cend() && r_iter != rhs.cend(); l_iter++, r_iter++) { - if (*l_iter < *r_iter) { - return true; - } - } - return lhs.size() < rhs.size(); - } -}; -} // namespace cs440