// commenting everything out when I commit so all commits my code technically // compiles #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; } // heavily referring to // https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal_of_a_black_non-root_leaf void complex_erase(Iterator pos) { Node *to_delete = &this->nodes[pos.ref.value()]; Node *parent = to_delete->par(); assert(parent != nullptr); Direction dir = parent->r() == to_delete ? Direction::Right : Direction::Left; Node *sibling; ; Node *close_nephew; Node *distant_nephew; parent->c_trans(dir, nullptr); do { dir = parent->r() == to_delete ? Direction::Right : Direction::Left; sibling = parent->child(!dir); distant_nephew = sibling->child(!dir); close_nephew = sibling->child(dir); if (sibling->color == Color::Red) { // case 3 parent->rotate(dir); parent->color = Color::Red; sibling->color = Color::Black; sibling = close_nephew; // redundant? distant_nephew = sibling->child(!dir); if (distant_nephew != nullptr && distant_nephew->color == Color::Red) { case6(parent, sibling, distant_nephew, dir); return; } close_nephew = sibling->child(dir); if (close_nephew != nullptr && close_nephew->color == Color::Red) { case5(parent, sibling, close_nephew, distant_nephew, dir); return; } sibling->color = Color::Red; parent->color = Color::Black; return; } if (distant_nephew != nullptr && distant_nephew->color == Color::Red) { case6(parent, sibling, distant_nephew, dir); return; } if (close_nephew != nullptr && close_nephew->color == Color::Red) { case5(parent, sibling, close_nephew, distant_nephew, dir); return; } if (parent->color == Color::Red) { // case 4 sibling->color = Color::Red; parent->color = Color::Black; return; } // case 2 sibling->color = Color::Red; to_delete = parent; parent = to_delete->par(); } while (parent != nullptr); } 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