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
#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) {