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
|
||||||
161
Map.hpp
161
Map.hpp
|
|
@ -5,12 +5,15 @@
|
||||||
// #define NDEBUG
|
// #define NDEBUG
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
namespace cs440 {
|
namespace cs440 {
|
||||||
// universal type defs here
|
// universal type defs here
|
||||||
namespace {
|
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 };
|
enum class Direction { Left, Right };
|
||||||
Direction operator!(Direction dir) {
|
Direction operator!(Direction dir) {
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
|
|
@ -278,30 +281,28 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
|
|
||||||
// cannot rotate nullptr
|
// cannot rotate nullptr
|
||||||
assert(this != nullptr);
|
assert(this != nullptr);
|
||||||
|
|
||||||
// we can't be root for this rotate operation
|
// we can't be root for this rotate operation
|
||||||
assert(this->parent != nullptr);
|
assert(this->parent != nullptr);
|
||||||
|
|
||||||
// if we're missing the child on the opposite direction this is an invalid
|
// if we're missing the child on the opposite direction this is an invalid
|
||||||
// rotation
|
// rotation
|
||||||
assert(this->child(!dir));
|
assert(this->child(!dir));
|
||||||
|
|
||||||
|
Direction m_dir = this->parent->which_child(this);
|
||||||
// gotta pull outselves out of parent to avoid accidentally overwriting
|
// gotta pull outselves out of parent to avoid accidentally overwriting
|
||||||
// outselves
|
// outselves
|
||||||
std::unique_ptr<Node> self =
|
std::unique_ptr<Node> self = this->parent->uchild(m_dir);
|
||||||
this->parent->uchild(this->parent->which_child(this));
|
|
||||||
|
|
||||||
// make sure this is actually us
|
// make sure this is actually us
|
||||||
assert(self.get() == this);
|
assert(self.get() == this);
|
||||||
|
|
||||||
// make our former position the position of the relevant child
|
// 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
|
// 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
|
// 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
|
// Referencing
|
||||||
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Notes_to_the_insert_diagrams
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dir = parent->which_child(self);
|
||||||
// if this is violated it's a bug
|
// if this is violated it's a bug
|
||||||
assert(parent->child(dir) == self);
|
assert(parent->child(dir) == self);
|
||||||
|
|
||||||
|
|
@ -334,6 +336,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dir = parent->which_child(self);
|
||||||
// table showing transforms on wikipedia doesn't have this so if it
|
// table showing transforms on wikipedia doesn't have this so if it
|
||||||
// happens it's probably a bug
|
// happens it's probably a bug
|
||||||
assert(grandparent->color == Color::Black);
|
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)) {
|
if (parent->which_child(self) != grandparent->which_child(parent)) {
|
||||||
// we're an inner child
|
// we're an inner child
|
||||||
// case 5
|
// case 5
|
||||||
parent->rotate(dir);
|
parent->rotate(!dir);
|
||||||
self = parent;
|
self = parent;
|
||||||
parent = 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
|
// recolor first so we aren't recoloring a dropped reference or smth
|
||||||
parent->color = Color::Black;
|
parent->color = Color::Black;
|
||||||
grandparent->color = Color::Red;
|
grandparent->color = Color::Red;
|
||||||
if (grandparent->parent == nullptr) {
|
if (grandparent->parent) {
|
||||||
map->rotate_root(!dir);
|
grandparent->rotate(!grandparent->which_child(parent));
|
||||||
} else {
|
} else {
|
||||||
grandparent->rotate(!dir);
|
map->rotate_root(!grandparent->which_child(parent));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -370,7 +373,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
};
|
};
|
||||||
|
|
||||||
// data needed for implementation
|
// data needed for implementation
|
||||||
std::optional<Node> root;
|
std::unique_ptr<Node> root;
|
||||||
std::size_t _size;
|
std::size_t _size;
|
||||||
Node *min;
|
Node *min;
|
||||||
Node *max;
|
Node *max;
|
||||||
|
|
@ -526,12 +529,21 @@ public:
|
||||||
}
|
}
|
||||||
const ValueType &operator*() const { return this->underlying.operator*(); }
|
const ValueType &operator*() const { return this->underlying.operator*(); }
|
||||||
const ValueType *operator->() const { return &this->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() : root{}, _size{0}, min{nullptr}, max{nullptr} {}
|
||||||
Map(const Map &rhs) : root{rhs.root}, _size{rhs._size} {
|
Map(const Map &rhs)
|
||||||
this->min = &this->root.value();
|
: root{std::make_unique<Node>(*rhs.root)}, _size{rhs._size} {
|
||||||
this->max = &this->root.value();
|
this->min = this->root.get();
|
||||||
|
this->max = this->root.get();
|
||||||
while (min->left) {
|
while (min->left) {
|
||||||
min = min->left.get();
|
min = min->left.get();
|
||||||
}
|
}
|
||||||
|
|
@ -540,8 +552,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map(Map &&rhs) : root{std::move(rhs.root)}, _size{rhs._size} {
|
Map(Map &&rhs) : root{std::move(rhs.root)}, _size{rhs._size} {
|
||||||
this->min = &this->root.value();
|
this->min = this->root.get();
|
||||||
this->max = &this->root.value();
|
this->max = this->root.get();
|
||||||
while (min->left) {
|
while (min->left) {
|
||||||
min = min->left.get();
|
min = min->left.get();
|
||||||
}
|
}
|
||||||
|
|
@ -550,10 +562,10 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map &operator=(const Map &rhs) {
|
Map &operator=(const Map &rhs) {
|
||||||
this->root = rhs.root;
|
this->root = std::make_unique<Node>(*rhs.root);
|
||||||
this->_size = rhs._size;
|
this->_size = rhs._size;
|
||||||
this->min = &this->root.value();
|
this->min = this->root.get();
|
||||||
this->max = &this->root.value();
|
this->max = this->root.get();
|
||||||
while (min->left) {
|
while (min->left) {
|
||||||
min = min->left.get();
|
min = min->left.get();
|
||||||
}
|
}
|
||||||
|
|
@ -565,8 +577,8 @@ public:
|
||||||
Map &operator=(Map &&rhs) {
|
Map &operator=(Map &&rhs) {
|
||||||
this->root = std::move(rhs.root);
|
this->root = std::move(rhs.root);
|
||||||
this->_size = rhs._size;
|
this->_size = rhs._size;
|
||||||
this->min = &this->root.value();
|
this->min = this->root.get();
|
||||||
this->max = &this->root.value();
|
this->max = this->root.get();
|
||||||
while (min->left) {
|
while (min->left) {
|
||||||
min = min->left.get();
|
min = min->left.get();
|
||||||
}
|
}
|
||||||
|
|
@ -578,27 +590,24 @@ public:
|
||||||
Map(std::initializer_list<std::pair<const Key_T, Mapped_T>> items) : Map{} {
|
Map(std::initializer_list<std::pair<const Key_T, Mapped_T>> items) : Map{} {
|
||||||
this->insert(items.begin(), items.end());
|
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; }
|
std::size_t size() const { return this->_size; }
|
||||||
bool empty() const { return this->size() == 0; }
|
bool empty() const { return this->size() == 0; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// private helpers
|
// private helpers
|
||||||
void rotate_root(Direction dir) {
|
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
|
// can't make null the new root
|
||||||
assert(new_root);
|
assert(new_root);
|
||||||
|
|
||||||
std::unique_ptr<Node> old_root =
|
std::unique_ptr<Node> old_root = std::move(root);
|
||||||
std::make_unique<Node>(std::move(root.value()));
|
|
||||||
|
|
||||||
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) {
|
if (old_root->left) {
|
||||||
old_root->left->parent = old_root.get();
|
old_root->left->parent = old_root.get();
|
||||||
|
|
@ -606,43 +615,44 @@ private:
|
||||||
if (old_root->right) {
|
if (old_root->right) {
|
||||||
old_root->right->parent = old_root.get();
|
old_root->right->parent = old_root.get();
|
||||||
}
|
}
|
||||||
if (old_root->next) {
|
// if (old_root->next) {
|
||||||
old_root->next->prev = old_root.get();
|
// old_root->next->prev = old_root.get();
|
||||||
|
// }
|
||||||
|
// if (old_root->prev) {
|
||||||
|
// old_root->prev->next = old_root.get();
|
||||||
|
// }
|
||||||
|
|
||||||
|
root->set_child(dir, std::move(old_root));
|
||||||
|
root->child(dir)->restore_ordering();
|
||||||
|
|
||||||
|
if (root->left) {
|
||||||
|
root->left->parent = root.get();
|
||||||
|
if (min == root.get()) {
|
||||||
|
min = root->left.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (root->right) {
|
||||||
|
root->right->parent = root.get();
|
||||||
|
if (max == root.get()) {
|
||||||
|
max = root->right.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();
|
|
||||||
|
|
||||||
if (root.value().left) {
|
|
||||||
root.value().left->parent = &root.value();
|
|
||||||
if (min == &root.value()) {
|
|
||||||
min = root.value().left.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (root.value().right) {
|
|
||||||
root.value().right->parent = &root.value();
|
|
||||||
if (max == &root.value()) {
|
|
||||||
max = root.value().right.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <bool trace = false>
|
template <bool trace = false>
|
||||||
std::pair<Node const *, Direction> locate(const Key_T &key) const {
|
std::pair<Node const *, Direction> locate(const Key_T &key) const {
|
||||||
Node const *ret_parent;
|
Node const *ret_parent;
|
||||||
Direction ret_dir;
|
Direction ret_dir;
|
||||||
// map is empty
|
// map is empty
|
||||||
if (!this->root.has_value()) {
|
if (!this->root) {
|
||||||
return std::make_pair(nullptr, ret_dir);
|
return std::make_pair(nullptr, ret_dir);
|
||||||
}
|
}
|
||||||
// value is in root
|
// value is in root
|
||||||
if (this->root.value().val->first == key) {
|
if (this->root->val->first == key) {
|
||||||
|
|
||||||
return std::make_pair(nullptr, ret_dir);
|
return std::make_pair(nullptr, ret_dir);
|
||||||
}
|
}
|
||||||
ret_parent = &this->root.value();
|
ret_parent = this->root.get();
|
||||||
if (key < ret_parent->val->first) {
|
if (key < ret_parent->val->first) {
|
||||||
ret_dir = Direction::Left;
|
ret_dir = Direction::Left;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -690,7 +700,7 @@ private:
|
||||||
// bookkeeping
|
// bookkeeping
|
||||||
redcheck(sibling_color) {
|
redcheck(sibling_color) {
|
||||||
// case 3
|
// case 3
|
||||||
if (parent->parent != nullptr) {
|
if (parent->parent) {
|
||||||
parent->rotate(dir);
|
parent->rotate(dir);
|
||||||
} else {
|
} else {
|
||||||
parent->map->rotate_root(dir);
|
parent->map->rotate_root(dir);
|
||||||
|
|
@ -727,7 +737,7 @@ private:
|
||||||
assert(parent);
|
assert(parent);
|
||||||
assert(sibling);
|
assert(sibling);
|
||||||
assert(distant);
|
assert(distant);
|
||||||
if (parent->parent != nullptr) {
|
if (parent->parent) {
|
||||||
parent->rotate(dir);
|
parent->rotate(dir);
|
||||||
} else {
|
} else {
|
||||||
parent->map->rotate_root(dir);
|
parent->map->rotate_root(dir);
|
||||||
|
|
@ -787,7 +797,7 @@ private:
|
||||||
}
|
}
|
||||||
// no children and root
|
// no children and root
|
||||||
else if (erasing->parent == nullptr) {
|
else if (erasing->parent == nullptr) {
|
||||||
erasing->map->root = std::nullopt;
|
erasing->map->root = nullptr;
|
||||||
}
|
}
|
||||||
// no children and red
|
// no children and red
|
||||||
else if (erasing->color == Color::Red) {
|
else if (erasing->color == Color::Red) {
|
||||||
|
|
@ -805,9 +815,9 @@ public:
|
||||||
Iterator find(const Key_T &key) {
|
Iterator find(const Key_T &key) {
|
||||||
auto [parent, dir] = locate(key);
|
auto [parent, dir] = locate(key);
|
||||||
if (parent == nullptr) {
|
if (parent == nullptr) {
|
||||||
if (this->root.has_value()) {
|
if (this->root) {
|
||||||
if (this->root.value().val->first == key) {
|
if (this->root->val->first == key) {
|
||||||
return Iterator{&this->root.value()};
|
return Iterator{this->root.get()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this->end();
|
return this->end();
|
||||||
|
|
@ -820,9 +830,9 @@ public:
|
||||||
ConstIterator find(const Key_T &key) const {
|
ConstIterator find(const Key_T &key) const {
|
||||||
auto [parent, dir] = locate(key);
|
auto [parent, dir] = locate(key);
|
||||||
if (parent == nullptr) {
|
if (parent == nullptr) {
|
||||||
if (this->root.has_value()) {
|
if (this->root) {
|
||||||
if (this->root.value().val->first == key) {
|
if (this->root->val->first == key) {
|
||||||
return Iterator{const_cast<Node *>(&this->root.value())};
|
return Iterator{const_cast<Node *>(this->root.get())};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this->end();
|
return this->end();
|
||||||
|
|
@ -840,15 +850,15 @@ public:
|
||||||
auto [parent, dir] = locate(key);
|
auto [parent, dir] = locate(key);
|
||||||
// located root node
|
// located root node
|
||||||
if (parent == nullptr) {
|
if (parent == nullptr) {
|
||||||
if (this->root.has_value()) {
|
if (this->root) {
|
||||||
return std::make_pair(Iterator{&root.value()}, false);
|
return std::make_pair(Iterator{root.get()}, false);
|
||||||
} else {
|
} else {
|
||||||
this->root = Node{val, this};
|
this->root = std::make_unique<Node>(Node{val, this});
|
||||||
this->root.value().color = Color::Black;
|
this->root->color = Color::Black;
|
||||||
this->min = &this->root.value();
|
this->min = this->root.get();
|
||||||
this->max = &this->root.value();
|
this->max = this->root.get();
|
||||||
this->_size++;
|
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}))
|
->set_child(dir, std::make_unique<Node>(Node{val, m}))
|
||||||
.get();
|
.get();
|
||||||
new_node->restore_red_black_insert(dir);
|
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();
|
new_node->restore_ordering();
|
||||||
if (this->min == parent && dir == Direction::Left) {
|
if (this->min == parent && dir == Direction::Left) {
|
||||||
this->min = new_node;
|
this->min = new_node;
|
||||||
|
|
@ -916,7 +929,7 @@ public:
|
||||||
|
|
||||||
// misc that can be implemented with the above or trivially
|
// misc that can be implemented with the above or trivially
|
||||||
void clear() {
|
void clear() {
|
||||||
this->root = std::move(std::nullopt);
|
this->root = std::move(nullptr);
|
||||||
this->_size = 0;
|
this->_size = 0;
|
||||||
}
|
}
|
||||||
Mapped_T &at(const Key_T &key) {
|
Mapped_T &at(const Key_T &key) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue