diff --git a/Map.hpp b/Map.hpp index b68f8bd..e1d091f 100644 --- a/Map.hpp +++ b/Map.hpp @@ -1,6 +1,8 @@ #ifndef _POWELL_CS440 #define _POWELL_CS440 #include +// uncomment on submission/performance test +// #define NDEBUG #include #include #include @@ -20,9 +22,6 @@ Direction operator!(Direction dir) { assert(false); } } -int x5 = 5; -int x6 = 6; -int x7 = 7; enum class Color { Red, Black }; } // namespace template class Map { @@ -31,6 +30,7 @@ template class Map { using internal_ValueType = std::pair; struct Node { + int valid = 0x13371337; Node *parent; internal_ValueType val; std::unique_ptr left; @@ -58,6 +58,10 @@ template class Map { : parent{nullptr}, val{std::move(rhs.val)}, left{std::move(rhs.left)}, right{std::move(rhs.right)}, color{rhs.color}, prev{nullptr}, next{nullptr}, map{rhs.map} { + if (rhs.valid != 0x13371337) { + std::cerr << "(" << rhs.val.first << ")" << std::endl; + } + rhs.valid = 0; if (this->left) { this->left->parent = this; } @@ -171,8 +175,13 @@ template class Map { // intuitively should be correct but might need to do restore ordering on // both instead - dropping->prev->next = dropping->next; - dropping->next->prev = dropping->prev; + if (dropping->prev != nullptr) { + dropping->prev->next = dropping->next; + } + + if (dropping->next != nullptr) { + dropping->next->prev = dropping->prev; + } } void restore_ordering() { this->prev = this->calc_pred(); @@ -246,7 +255,6 @@ template class Map { // make ourselves our former child's child this->parent->child(!dir)->set_child(dir, std::move(self)); } - // TODO: // Referencing // https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Notes_to_the_insert_diagrams void restore_red_black_insert(Direction dir) { @@ -310,8 +318,6 @@ template class Map { self = grandparent; } } - // TODO: - void restore_red_black_erase() {} }; // data needed for implementation @@ -332,6 +338,15 @@ public: public: friend Map; Iterator() = delete; + void check() { + assert(underlying->val.first < 200); + if (underlying->prev != nullptr) { + assert(underlying->prev->val.first < 200); + } + if (underlying->next != nullptr) { + assert(underlying->next->val.first < 200); + } + } }; Map() : root{}, _size{0} {} Map(const Map &rhs) : root{rhs.root}, _size{rhs._size} {} @@ -362,7 +377,7 @@ private: root.value() = std::move(*new_root); - old_root->set_child(!dir, std::move(root.value().uchild(dir))); + old_root->set_child(!dir, root.value().uchild(dir)); root.value().set_child(dir, std::move(old_root)); } template @@ -420,6 +435,131 @@ private: } return std::make_pair(ret_parent, ret_dir); } + void hard_erase(Node *n) { + assert(n->parent); + Node *parent = n->parent; + Direction dir = parent->which_child(n); + parent->erase_child(n); + goto skip; + while (true) { + parent = n->parent; + if (parent == nullptr) { + // we're at root we're done (case 1) + return; + } + dir = parent->which_child(n); + skip: + Color par_color = parent->color; + + Node *sibling = parent->child(!dir); + Color sibling_color = sibling ? sibling->color : Color::Black; + + Node *close = sibling ? sibling->child(dir) : nullptr; + Node *distant = sibling ? sibling->child(!dir) : nullptr; + + Color close_color = close ? close->color : Color::Black; + Color distant_color = distant ? distant->color : Color::Black; + +#define redcheck(v) if ((v) == Color::Red) + // it kinda sucks but I think that goto is genuinely the best solution + // here, making methods for cases 4,5 and 6 is a lot of unneeded + // bookkeeping + redcheck(sibling_color) { + // case 3 + if (parent->parent != nullptr) { + parent->rotate(dir); + } else { + parent->map->rotate_root(dir); + } + parent->color = Color::Red; + sibling->color = Color::Black; + sibling = close; + + distant = sibling->child(!dir); + if (distant != nullptr && distant->color == Color::Red) { + goto case_6; + } + close = sibling->child(dir); + if (close != nullptr && close->color == Color::Red) { + goto case_5; + } + goto case_4; + } + else redcheck(close_color) { + // case 5 + case_5: + assert(sibling); + assert(close); + sibling->rotate(!dir); + sibling->color = Color::Red; + close->color = Color::Black; + distant = sibling; + sibling = close; + goto case_6; + } + else redcheck(distant_color) { + // case 6 + case_6: + assert(parent); + assert(sibling); + assert(distant); + if (parent->parent != nullptr) { + parent->rotate(dir); + } else { + parent->map->rotate_root(dir); + } + Color tmp = parent->color; + sibling->color = tmp; + parent->color = Color::Black; + distant->color = Color::Black; + return; + } + else redcheck(par_color) { + // case 4 + case_4: + assert(sibling); + sibling->color = Color::Red; + parent->color = Color::Black; + return; + } + else { + // case 2 + assert(sibling); + sibling->color = Color::Red; + n = parent; + continue; + } + } + } + bool core_erase(Node *erasing) { + // 2 children + if (erasing->left && erasing->right) { + Node *succ = erasing->next; + erasing->val = succ->val; + this->core_erase(succ); + } + // 1 child + else if (erasing->left) { + *erasing = std::move(*erasing->left); + return true; + } else if (erasing->right) { + *erasing = std::move(*erasing->right); + return true; + } + // no children and root + else if (!erasing->parent) { + erasing->map->root = std::nullopt; + } + // no children and red + else if (erasing->color == Color::Red) { + erasing->parent->erase_child(erasing); + } + // no children and black + else { + hard_erase(erasing); + } + return false; + } public: // baseline find using locate @@ -483,9 +623,25 @@ public: return std::make_pair(Iterator{new_node}, true); } void erase(Iterator pos) { - Node *p = pos.underlying->parent; - p->erase_child(pos.underlying); - p->restore_red_black_erase(); + Node *before = pos.underlying->prev; + Node *after = pos.underlying->next; + + if (core_erase(pos.underlying)) { + pos.underlying->restore_ordering(); + } else { + if (before != nullptr) { + before->next = before->calc_succ(); + if (before->next != nullptr) { + before->next->prev = before; + } + } + if (after != nullptr) { + after->prev = after->calc_pred(); + if (after->prev != nullptr) { + after->prev->next = after; + } + } + } } // baseline iterator creation diff --git a/t.cpp b/t.cpp index c23c742..099a42c 100644 --- a/t.cpp +++ b/t.cpp @@ -3,9 +3,6 @@ template class cs440::Map; int main(void) { cs440::Map a; - int x5 = 5; - int x6 = 6; - int x7 = 7; a.insert({1, 1}); a.insert({2, 2}); a.insert({3, 3}); @@ -16,49 +13,10 @@ int main(void) { a.insert({8, 8}); a.insert({9, 9}); a.insert({10, 10}); - a.insert({11, 11}); - a.insert({12, 12}); - a.insert({13, 13}); - a.insert({14, 14}); - a.insert({15, 15}); - a.insert({16, 16}); - a.insert({17, 17}); - a.insert({18, 18}); - a.insert({19, 19}); - a.insert({20, 20}); - a.insert({21, 21}); - a.insert({22, 22}); - a.insert({23, 23}); - a.insert({24, 24}); - a.insert({25, 25}); - a.insert({26, 26}); - a.insert({27, 27}); - a.insert({28, 28}); - a.insert({29, 29}); - a.insert({30, 30}); - a.insert({31, 31}); - a.insert({32, 32}); - a.insert({33, 33}); - a.insert({34, 34}); - a.insert({35, 35}); - a.insert({36, 36}); - a.insert({37, 37}); - a.insert({38, 38}); - a.insert({39, 39}); - a.insert({40, 40}); - a.insert({41, 41}); - a.insert({42, 42}); - a.insert({43, 43}); - a.insert({44, 44}); - a.insert({45, 45}); - a.insert({46, 46}); - a.insert({47, 47}); - a.insert({48, 48}); - a.insert({49, 49}); - a.insert({50, 50}); - for (std::size_t i = 1; i <= 50; i++) { - std::cout << i << "\t"; - a.find_trace(i); + for (std::size_t i = 1; i <= 10; i++) { + // a.find(i).check(); + std::cout << i << std::endl; + a.erase(a.find(i)); } return 0; }