Fixed issues and setup a makefile for submission
This commit is contained in:
parent
30babdb9dc
commit
ae0b45df73
2 changed files with 100 additions and 70 deletions
17
Makefile
Normal file
17
Makefile
Normal file
|
@ -0,0 +1,17 @@
|
|||
.PHONY: all
|
||||
all: minimal morse perf test test-kec
|
||||
|
||||
minimal: Map.hpp minimal.cpp
|
||||
g++ minimal.cpp -o minimal
|
||||
|
||||
morse: Map.hpp morseex.cpp
|
||||
g++ morseex.cpp -o morse
|
||||
|
||||
perf: Map.hpp test-scaling.cpp
|
||||
g++ test-scaling.cpp -o perf
|
||||
|
||||
test: Map.hpp test.cpp
|
||||
g++ test.cpp -o test
|
||||
|
||||
test-kec: Map.hpp test-kec.cpp
|
||||
g++ test-kec.cpp -o test-kec
|
153
Map.hpp
153
Map.hpp
|
@ -5,12 +5,15 @@
|
|||
// #define NDEBUG
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
namespace cs440 {
|
||||
// universal type defs here
|
||||
namespace {
|
||||
// technically having this stuff here instead of in a C++ file is wasteful but
|
||||
// 1) I'm lazy
|
||||
// 2) idk how to make the Color enum value an implementation detail which the
|
||||
// template can use but external code can't other than this
|
||||
enum class Direction { Left, Right };
|
||||
Direction operator!(Direction dir) {
|
||||
switch (dir) {
|
||||
|
@ -278,30 +281,28 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
|
||||
// cannot rotate nullptr
|
||||
assert(this != nullptr);
|
||||
|
||||
// we can't be root for this rotate operation
|
||||
assert(this->parent != nullptr);
|
||||
|
||||
// if we're missing the child on the opposite direction this is an invalid
|
||||
// rotation
|
||||
assert(this->child(!dir));
|
||||
|
||||
Direction m_dir = this->parent->which_child(this);
|
||||
// gotta pull outselves out of parent to avoid accidentally overwriting
|
||||
// outselves
|
||||
std::unique_ptr<Node> self =
|
||||
this->parent->uchild(this->parent->which_child(this));
|
||||
std::unique_ptr<Node> self = this->parent->uchild(m_dir);
|
||||
|
||||
// make sure this is actually us
|
||||
assert(self.get() == this);
|
||||
|
||||
// make our former position the position of the relevant child
|
||||
this->parent->set_child(!dir, this->uchild(!dir));
|
||||
this->parent->set_child(m_dir, this->uchild(!dir));
|
||||
|
||||
// steal our former child's child
|
||||
this->set_child(!dir, this->parent->child(!dir)->uchild(dir));
|
||||
this->set_child(!dir, this->parent->child(m_dir)->uchild(dir));
|
||||
|
||||
// make ourselves our former child's child
|
||||
this->parent->child(!dir)->set_child(dir, std::move(self));
|
||||
this->parent->child(m_dir)->set_child(dir, std::move(self));
|
||||
}
|
||||
// Referencing
|
||||
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Notes_to_the_insert_diagrams
|
||||
|
@ -318,6 +319,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
return;
|
||||
}
|
||||
|
||||
dir = parent->which_child(self);
|
||||
// if this is violated it's a bug
|
||||
assert(parent->child(dir) == self);
|
||||
|
||||
|
@ -334,6 +336,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
return;
|
||||
}
|
||||
|
||||
dir = parent->which_child(self);
|
||||
// table showing transforms on wikipedia doesn't have this so if it
|
||||
// happens it's probably a bug
|
||||
assert(grandparent->color == Color::Black);
|
||||
|
@ -343,7 +346,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
if (parent->which_child(self) != grandparent->which_child(parent)) {
|
||||
// we're an inner child
|
||||
// case 5
|
||||
parent->rotate(dir);
|
||||
parent->rotate(!dir);
|
||||
self = parent;
|
||||
parent = self->parent;
|
||||
}
|
||||
|
@ -352,10 +355,10 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
// recolor first so we aren't recoloring a dropped reference or smth
|
||||
parent->color = Color::Black;
|
||||
grandparent->color = Color::Red;
|
||||
if (grandparent->parent == nullptr) {
|
||||
map->rotate_root(!dir);
|
||||
if (grandparent->parent) {
|
||||
grandparent->rotate(!grandparent->which_child(parent));
|
||||
} else {
|
||||
grandparent->rotate(!dir);
|
||||
map->rotate_root(!grandparent->which_child(parent));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -370,7 +373,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
};
|
||||
|
||||
// data needed for implementation
|
||||
std::optional<Node> root;
|
||||
std::unique_ptr<Node> root;
|
||||
std::size_t _size;
|
||||
Node *min;
|
||||
Node *max;
|
||||
|
@ -526,12 +529,21 @@ public:
|
|||
}
|
||||
const ValueType &operator*() const { return this->underlying.operator*(); }
|
||||
const ValueType *operator->() const { return &this->operator*(); }
|
||||
friend bool operator==(const ReverseIterator &lhs,
|
||||
const ReverseIterator &rhs) {
|
||||
return lhs.underlying == rhs.underlying;
|
||||
}
|
||||
friend bool operator!=(const ReverseIterator &lhs,
|
||||
const ReverseIterator &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
Map() : root{}, _size{0} {}
|
||||
Map(const Map &rhs) : root{rhs.root}, _size{rhs._size} {
|
||||
this->min = &this->root.value();
|
||||
this->max = &this->root.value();
|
||||
Map() : root{}, _size{0}, min{nullptr}, max{nullptr} {}
|
||||
Map(const Map &rhs)
|
||||
: root{std::make_unique<Node>(*rhs.root)}, _size{rhs._size} {
|
||||
this->min = this->root.get();
|
||||
this->max = this->root.get();
|
||||
while (min->left) {
|
||||
min = min->left.get();
|
||||
}
|
||||
|
@ -540,8 +552,8 @@ public:
|
|||
}
|
||||
}
|
||||
Map(Map &&rhs) : root{std::move(rhs.root)}, _size{rhs._size} {
|
||||
this->min = &this->root.value();
|
||||
this->max = &this->root.value();
|
||||
this->min = this->root.get();
|
||||
this->max = this->root.get();
|
||||
while (min->left) {
|
||||
min = min->left.get();
|
||||
}
|
||||
|
@ -550,10 +562,10 @@ public:
|
|||
}
|
||||
}
|
||||
Map &operator=(const Map &rhs) {
|
||||
this->root = rhs.root;
|
||||
this->root = std::make_unique<Node>(*rhs.root);
|
||||
this->_size = rhs._size;
|
||||
this->min = &this->root.value();
|
||||
this->max = &this->root.value();
|
||||
this->min = this->root.get();
|
||||
this->max = this->root.get();
|
||||
while (min->left) {
|
||||
min = min->left.get();
|
||||
}
|
||||
|
@ -565,8 +577,8 @@ public:
|
|||
Map &operator=(Map &&rhs) {
|
||||
this->root = std::move(rhs.root);
|
||||
this->_size = rhs._size;
|
||||
this->min = &this->root.value();
|
||||
this->max = &this->root.value();
|
||||
this->min = this->root.get();
|
||||
this->max = this->root.get();
|
||||
while (min->left) {
|
||||
min = min->left.get();
|
||||
}
|
||||
|
@ -578,27 +590,24 @@ public:
|
|||
Map(std::initializer_list<std::pair<const Key_T, Mapped_T>> items) : Map{} {
|
||||
this->insert(items.begin(), items.end());
|
||||
}
|
||||
void check() {
|
||||
assert(!this->root || this->root.value().color == Color::Black);
|
||||
}
|
||||
std::size_t size() const { return this->_size; }
|
||||
bool empty() const { return this->size() == 0; }
|
||||
|
||||
private:
|
||||
// private helpers
|
||||
void rotate_root(Direction dir) {
|
||||
assert(root.has_value());
|
||||
assert(root);
|
||||
|
||||
std::unique_ptr<Node> new_root = root.value().uchild(!dir);
|
||||
std::unique_ptr<Node> new_root = root->uchild(!dir);
|
||||
// can't make null the new root
|
||||
assert(new_root);
|
||||
|
||||
std::unique_ptr<Node> old_root =
|
||||
std::make_unique<Node>(std::move(root.value()));
|
||||
std::unique_ptr<Node> old_root = std::move(root);
|
||||
|
||||
root.value() = std::move(*new_root.release());
|
||||
root = std::move(new_root);
|
||||
root->parent = nullptr;
|
||||
|
||||
old_root->set_child(!dir, root.value().uchild(dir));
|
||||
old_root->set_child(!dir, root->uchild(dir));
|
||||
|
||||
if (old_root->left) {
|
||||
old_root->left->parent = old_root.get();
|
||||
|
@ -606,43 +615,44 @@ private:
|
|||
if (old_root->right) {
|
||||
old_root->right->parent = old_root.get();
|
||||
}
|
||||
if (old_root->next) {
|
||||
old_root->next->prev = old_root.get();
|
||||
}
|
||||
if (old_root->prev) {
|
||||
old_root->prev->next = old_root.get();
|
||||
}
|
||||
// if (old_root->next) {
|
||||
// old_root->next->prev = old_root.get();
|
||||
// }
|
||||
// if (old_root->prev) {
|
||||
// old_root->prev->next = old_root.get();
|
||||
// }
|
||||
|
||||
root.value().set_child(dir, std::move(old_root));
|
||||
root.value().child(dir)->restore_ordering();
|
||||
root->set_child(dir, std::move(old_root));
|
||||
root->child(dir)->restore_ordering();
|
||||
|
||||
if (root.value().left) {
|
||||
root.value().left->parent = &root.value();
|
||||
if (min == &root.value()) {
|
||||
min = root.value().left.get();
|
||||
if (root->left) {
|
||||
root->left->parent = root.get();
|
||||
if (min == root.get()) {
|
||||
min = root->left.get();
|
||||
}
|
||||
}
|
||||
if (root.value().right) {
|
||||
root.value().right->parent = &root.value();
|
||||
if (max == &root.value()) {
|
||||
max = root.value().right.get();
|
||||
if (root->right) {
|
||||
root->right->parent = root.get();
|
||||
if (max == root.get()) {
|
||||
max = root->right.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool trace = false>
|
||||
std::pair<Node const *, Direction> locate(const Key_T &key) const {
|
||||
Node const *ret_parent;
|
||||
Direction ret_dir;
|
||||
// map is empty
|
||||
if (!this->root.has_value()) {
|
||||
if (!this->root) {
|
||||
return std::make_pair(nullptr, ret_dir);
|
||||
}
|
||||
// value is in root
|
||||
if (this->root.value().val->first == key) {
|
||||
if (this->root->val->first == key) {
|
||||
|
||||
return std::make_pair(nullptr, ret_dir);
|
||||
}
|
||||
ret_parent = &this->root.value();
|
||||
ret_parent = this->root.get();
|
||||
if (key < ret_parent->val->first) {
|
||||
ret_dir = Direction::Left;
|
||||
} else {
|
||||
|
@ -690,7 +700,7 @@ private:
|
|||
// bookkeeping
|
||||
redcheck(sibling_color) {
|
||||
// case 3
|
||||
if (parent->parent != nullptr) {
|
||||
if (parent->parent) {
|
||||
parent->rotate(dir);
|
||||
} else {
|
||||
parent->map->rotate_root(dir);
|
||||
|
@ -727,7 +737,7 @@ private:
|
|||
assert(parent);
|
||||
assert(sibling);
|
||||
assert(distant);
|
||||
if (parent->parent != nullptr) {
|
||||
if (parent->parent) {
|
||||
parent->rotate(dir);
|
||||
} else {
|
||||
parent->map->rotate_root(dir);
|
||||
|
@ -787,7 +797,7 @@ private:
|
|||
}
|
||||
// no children and root
|
||||
else if (erasing->parent == nullptr) {
|
||||
erasing->map->root = std::nullopt;
|
||||
erasing->map->root = nullptr;
|
||||
}
|
||||
// no children and red
|
||||
else if (erasing->color == Color::Red) {
|
||||
|
@ -805,9 +815,9 @@ public:
|
|||
Iterator find(const Key_T &key) {
|
||||
auto [parent, dir] = locate(key);
|
||||
if (parent == nullptr) {
|
||||
if (this->root.has_value()) {
|
||||
if (this->root.value().val->first == key) {
|
||||
return Iterator{&this->root.value()};
|
||||
if (this->root) {
|
||||
if (this->root->val->first == key) {
|
||||
return Iterator{this->root.get()};
|
||||
}
|
||||
}
|
||||
return this->end();
|
||||
|
@ -820,9 +830,9 @@ public:
|
|||
ConstIterator find(const Key_T &key) const {
|
||||
auto [parent, dir] = locate(key);
|
||||
if (parent == nullptr) {
|
||||
if (this->root.has_value()) {
|
||||
if (this->root.value().val->first == key) {
|
||||
return Iterator{const_cast<Node *>(&this->root.value())};
|
||||
if (this->root) {
|
||||
if (this->root->val->first == key) {
|
||||
return Iterator{const_cast<Node *>(this->root.get())};
|
||||
}
|
||||
}
|
||||
return this->end();
|
||||
|
@ -840,15 +850,15 @@ public:
|
|||
auto [parent, dir] = locate(key);
|
||||
// located root node
|
||||
if (parent == nullptr) {
|
||||
if (this->root.has_value()) {
|
||||
return std::make_pair(Iterator{&root.value()}, false);
|
||||
if (this->root) {
|
||||
return std::make_pair(Iterator{root.get()}, false);
|
||||
} else {
|
||||
this->root = Node{val, this};
|
||||
this->root.value().color = Color::Black;
|
||||
this->min = &this->root.value();
|
||||
this->max = &this->root.value();
|
||||
this->root = std::make_unique<Node>(Node{val, this});
|
||||
this->root->color = Color::Black;
|
||||
this->min = this->root.get();
|
||||
this->max = this->root.get();
|
||||
this->_size++;
|
||||
return std::make_pair(Iterator{&root.value()}, true);
|
||||
return std::make_pair(Iterator{root.get()}, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -865,7 +875,10 @@ public:
|
|||
->set_child(dir, std::make_unique<Node>(Node{val, m}))
|
||||
.get();
|
||||
new_node->restore_red_black_insert(dir);
|
||||
|
||||
// if the new_node_ptr isn't valid anymore then it has to be root
|
||||
if (new_node->valid != 0x13371337) {
|
||||
new_node = this->root.get();
|
||||
}
|
||||
new_node->restore_ordering();
|
||||
if (this->min == parent && dir == Direction::Left) {
|
||||
this->min = new_node;
|
||||
|
@ -916,7 +929,7 @@ public:
|
|||
|
||||
// misc that can be implemented with the above or trivially
|
||||
void clear() {
|
||||
this->root = std::move(std::nullopt);
|
||||
this->root = std::move(nullptr);
|
||||
this->_size = 0;
|
||||
}
|
||||
Mapped_T &at(const Key_T &key) {
|
||||
|
|
Loading…
Reference in a new issue