Fixed issues and setup a makefile for submission

This commit is contained in:
Pagwin 2024-11-25 22:43:48 -05:00
parent 30babdb9dc
commit ae0b45df73
No known key found for this signature in database
GPG key ID: 81137023740CA260
2 changed files with 100 additions and 70 deletions

17
Makefile Normal file
View 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
View file

@ -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) { // if (old_root->prev) {
old_root->prev->next = old_root.get(); // old_root->prev->next = old_root.get();
} // }
root.value().set_child(dir, std::move(old_root)); root->set_child(dir, std::move(old_root));
root.value().child(dir)->restore_ordering(); root->child(dir)->restore_ordering();
if (root.value().left) { if (root->left) {
root.value().left->parent = &root.value(); root->left->parent = root.get();
if (min == &root.value()) { if (min == root.get()) {
min = root.value().left.get(); min = root->left.get();
} }
} }
if (root.value().right) { if (root->right) {
root.value().right->parent = &root.value(); root->right->parent = root.get();
if (max == &root.value()) { if (max == root.get()) {
max = root.value().right.get(); max = root->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) {