Compare commits
No commits in common. "ae0b45df73360da4e7e4c7388529d829f38ace6f" and "37aacdbae7f41aad58ab6a2c4eb7c5198ac4e029" have entirely different histories.
ae0b45df73
...
37aacdbae7
10 changed files with 142 additions and 2078 deletions
17
Makefile
17
Makefile
|
@ -1,17 +0,0 @@
|
|||
.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
|
558
Map.hpp
558
Map.hpp
|
@ -4,16 +4,13 @@
|
|||
// uncomment on submission/performance test
|
||||
// #define NDEBUG
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
#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) {
|
||||
|
@ -34,8 +31,8 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
|
||||
struct Node {
|
||||
int valid = 0x13371337;
|
||||
Node *parent = nullptr;
|
||||
std::unique_ptr<internal_ValueType> val;
|
||||
Node *parent;
|
||||
internal_ValueType val;
|
||||
std::unique_ptr<Node> left;
|
||||
std::unique_ptr<Node> right;
|
||||
Color color;
|
||||
|
@ -43,59 +40,44 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
Node *next;
|
||||
Map *map;
|
||||
Node(internal_ValueType val, Map *map)
|
||||
: parent{nullptr}, val{new internal_ValueType{val}}, left{}, right{},
|
||||
color{Color::Red}, prev{nullptr}, next{nullptr}, map{map} {}
|
||||
: parent{nullptr}, val{val}, left{}, right{}, color{Color::Red},
|
||||
prev{nullptr}, next{nullptr}, map{map} {}
|
||||
Node(const Node &rhs)
|
||||
: parent{nullptr},
|
||||
val{rhs.val ? new internal_ValueType{*rhs.val} : nullptr},
|
||||
left{rhs.left ? std::make_unique<Node>(*rhs.left) : nullptr},
|
||||
right{rhs.right ? std::make_unique<Node>(*rhs.right) : nullptr},
|
||||
color{rhs.color}, prev{nullptr}, next{nullptr}, map{rhs.map} {
|
||||
this->valid = 0x13371337;
|
||||
|
||||
: parent{nullptr}, val{rhs.val},
|
||||
left{std::make_unique<Node>(*rhs.left)},
|
||||
right{std::make_unique<Node>(*rhs.right)}, color{rhs.color},
|
||||
prev{nullptr}, next{nullptr}, map{rhs.map} {
|
||||
if (this->left) {
|
||||
this->left->parent = this;
|
||||
}
|
||||
if (this->right) {
|
||||
this->right->parent = this;
|
||||
}
|
||||
|
||||
this->next = rhs.next;
|
||||
this->prev = rhs.prev;
|
||||
}
|
||||
Node(Node &&rhs)
|
||||
: parent{nullptr}, val{std::move(rhs.val)}, left{std::move(rhs.left)},
|
||||
right{std::move(rhs.right)}, color{rhs.color}, prev{nullptr},
|
||||
next{nullptr}, map{rhs.map} {
|
||||
|
||||
if (rhs.valid != 0x13371337) {
|
||||
std::cerr << "(" << rhs.val.first << ")" << std::endl;
|
||||
}
|
||||
rhs.valid = 0;
|
||||
this->valid = 0x13371337;
|
||||
|
||||
if (this->left) {
|
||||
this->left->parent = this;
|
||||
}
|
||||
if (this->right) {
|
||||
this->right->parent = this;
|
||||
}
|
||||
|
||||
this->next = rhs.next;
|
||||
this->prev = rhs.prev;
|
||||
}
|
||||
~Node() {}
|
||||
Node &operator=(const Node &rhs) {
|
||||
// retain parent as is, common case is the copy or move is happening due
|
||||
// to a rotation where parent can get wonky
|
||||
// this->parent
|
||||
|
||||
this->val =
|
||||
rhs.val ? std::unique_ptr<internal_ValueType>{new internal_ValueType{
|
||||
*rhs.val}}
|
||||
: nullptr;
|
||||
this->left = rhs.left ? std::make_unique<Node>(*rhs.left) : nullptr;
|
||||
this->right = rhs.right ? std::make_unique<Node>(*rhs.right) : nullptr;
|
||||
this->val = rhs.val;
|
||||
this->left = std::make_unique<Node>(*rhs.left);
|
||||
this->right = std::make_unique<Node>(*rhs.right);
|
||||
this->color = rhs.color;
|
||||
this->valid = 0x13371337;
|
||||
|
||||
if (this->left) {
|
||||
this->left->parent = this;
|
||||
this->left->restore_ordering();
|
||||
|
@ -104,7 +86,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
this->right->parent = this;
|
||||
this->right->restore_ordering();
|
||||
}
|
||||
this->restore_ordering();
|
||||
|
||||
this->map = rhs.map;
|
||||
return *this;
|
||||
}
|
||||
|
@ -112,12 +94,10 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
// retain parent as is, common case is the copy or move is happening due
|
||||
// to a rotation where parent can get wonky
|
||||
// this->parent
|
||||
this->val = std::move(rhs.val);
|
||||
this->val = rhs.val;
|
||||
this->left = std::move(rhs.left);
|
||||
this->right = std::move(rhs.right);
|
||||
this->color = rhs.color;
|
||||
this->valid = 0x13371337;
|
||||
rhs.valid = 0;
|
||||
if (this->left) {
|
||||
this->left->parent = this;
|
||||
this->left->restore_ordering();
|
||||
|
@ -126,7 +106,6 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
this->right->parent = this;
|
||||
this->right->restore_ordering();
|
||||
}
|
||||
this->restore_ordering();
|
||||
this->map = rhs.map;
|
||||
return *this;
|
||||
}
|
||||
|
@ -140,16 +119,6 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
assert(false);
|
||||
}
|
||||
}
|
||||
Node const *child(Direction dir) const {
|
||||
switch (dir) {
|
||||
case Direction::Left:
|
||||
return this->left.get();
|
||||
case Direction::Right:
|
||||
return this->right.get();
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
std::unique_ptr<Node> uchild(Direction dir) {
|
||||
switch (dir) {
|
||||
case Direction::Left:
|
||||
|
@ -166,20 +135,15 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
std::unique_ptr<Node> &set_child(Direction dir,
|
||||
std::unique_ptr<Node> new_child) {
|
||||
|
||||
if (new_child) {
|
||||
new_child->parent = this;
|
||||
}
|
||||
|
||||
switch (dir) {
|
||||
case Direction::Left:
|
||||
|
||||
this->left = std::move(new_child);
|
||||
if (this->left) {
|
||||
this->left->parent = this;
|
||||
}
|
||||
return this->left;
|
||||
return this->left = std::move(new_child);
|
||||
case Direction::Right:
|
||||
this->right = std::move(new_child);
|
||||
if (this->right) {
|
||||
this->right->parent = this;
|
||||
}
|
||||
return this->right;
|
||||
return this->right = std::move(new_child);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
@ -195,8 +159,6 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
}
|
||||
void erase_child(Node *n) { this->erase_child(this->which_child(n)); }
|
||||
void erase_child(Direction dir) {
|
||||
bool minmax = this->child(dir) == this->map->min ||
|
||||
this->child(dir) == this->map->max;
|
||||
// bringing ownership to this function scope so Deleter gets called at end
|
||||
// of function and we can do reordering things
|
||||
std::unique_ptr<Node> dropping;
|
||||
|
@ -220,18 +182,6 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
if (dropping->next != nullptr) {
|
||||
dropping->next->prev = dropping->prev;
|
||||
}
|
||||
|
||||
if (minmax) {
|
||||
switch (dir) {
|
||||
case Direction::Left:
|
||||
this->map->min = this;
|
||||
break;
|
||||
case Direction::Right:
|
||||
this->map->max = this;
|
||||
break;
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
void restore_ordering() {
|
||||
this->prev = this->calc_pred();
|
||||
|
@ -281,28 +231,29 @@ 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(m_dir);
|
||||
std::unique_ptr<Node> self = this->parent->uchild(!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(m_dir, this->uchild(!dir));
|
||||
this->parent->set_child(!dir, this->uchild(!dir));
|
||||
|
||||
// steal our former child's child
|
||||
this->set_child(!dir, this->parent->child(m_dir)->uchild(dir));
|
||||
this->set_child(!dir, this->parent->child(!dir)->uchild(dir));
|
||||
|
||||
// make ourselves our former child's child
|
||||
this->parent->child(m_dir)->set_child(dir, std::move(self));
|
||||
this->parent->child(!dir)->set_child(dir, std::move(self));
|
||||
}
|
||||
// Referencing
|
||||
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Notes_to_the_insert_diagrams
|
||||
|
@ -315,11 +266,9 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
Node *parent = self->parent;
|
||||
// we're root, no-op (case 3)
|
||||
if (!parent) {
|
||||
self->color = Color::Black;
|
||||
return;
|
||||
}
|
||||
|
||||
dir = parent->which_child(self);
|
||||
// if this is violated it's a bug
|
||||
assert(parent->child(dir) == self);
|
||||
|
||||
|
@ -336,7 +285,6 @@ 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);
|
||||
|
@ -346,7 +294,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;
|
||||
}
|
||||
|
@ -355,10 +303,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) {
|
||||
grandparent->rotate(!grandparent->which_child(parent));
|
||||
if (grandparent->parent == nullptr) {
|
||||
map->rotate_root(!dir);
|
||||
} else {
|
||||
map->rotate_root(!grandparent->which_child(parent));
|
||||
grandparent->rotate(!dir);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -373,15 +321,13 @@ template <typename Key_T, typename Mapped_T> class Map {
|
|||
};
|
||||
|
||||
// data needed for implementation
|
||||
std::unique_ptr<Node> root;
|
||||
std::optional<Node> root;
|
||||
std::size_t _size;
|
||||
Node *min;
|
||||
Node *max;
|
||||
|
||||
public:
|
||||
friend Node;
|
||||
class ConstIterator;
|
||||
class ReverseIterator;
|
||||
// public type definitions
|
||||
class Iterator {
|
||||
Node *underlying;
|
||||
|
@ -391,282 +337,102 @@ public:
|
|||
|
||||
public:
|
||||
friend Map;
|
||||
friend ConstIterator;
|
||||
friend ReverseIterator;
|
||||
Iterator() = delete;
|
||||
Iterator(const Iterator &rhs) = default;
|
||||
Iterator &operator=(const Iterator &) = default;
|
||||
~Iterator() = default;
|
||||
// precrement
|
||||
Iterator &operator++() {
|
||||
if (this->underlying == nullptr) {
|
||||
this->underlying = this->store;
|
||||
this->store = nullptr;
|
||||
return *this;
|
||||
void check() {
|
||||
assert(underlying->val.first < 200);
|
||||
if (underlying->prev != nullptr) {
|
||||
assert(underlying->prev->val.first < 200);
|
||||
}
|
||||
if (this->underlying->next == nullptr) {
|
||||
this->store = this->underlying;
|
||||
if (underlying->next != nullptr) {
|
||||
assert(underlying->next->val.first < 200);
|
||||
}
|
||||
this->underlying = this->underlying->next;
|
||||
return *this;
|
||||
}
|
||||
// postcrement
|
||||
Iterator operator++(int) {
|
||||
auto copy = *this;
|
||||
this->operator++();
|
||||
return copy;
|
||||
}
|
||||
|
||||
// precrement
|
||||
Iterator &operator--() {
|
||||
if (this->underlying == nullptr) {
|
||||
this->underlying = this->store;
|
||||
this->store = nullptr;
|
||||
return *this;
|
||||
}
|
||||
if (this->underlying->prev == nullptr) {
|
||||
this->store = this->underlying;
|
||||
}
|
||||
this->underlying = this->underlying->prev;
|
||||
return *this;
|
||||
}
|
||||
// postcrement
|
||||
Iterator operator--(int) {
|
||||
auto copy = *this;
|
||||
this->operator--();
|
||||
return copy;
|
||||
}
|
||||
|
||||
ValueType &operator*() const {
|
||||
ValueType *ret = (ValueType *)(this->underlying->val.get());
|
||||
return *ret;
|
||||
}
|
||||
ValueType *operator->() const { return &this->operator*(); }
|
||||
friend bool operator==(const Iterator &lhs, const Iterator &rhs) {
|
||||
return lhs.underlying == rhs.underlying;
|
||||
}
|
||||
friend bool operator!=(const Iterator &lhs, const Iterator &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
class ConstIterator {
|
||||
Iterator underlying;
|
||||
|
||||
public:
|
||||
friend Map;
|
||||
ConstIterator() = delete;
|
||||
ConstIterator(const Iterator &underlying) : underlying{underlying} {}
|
||||
ConstIterator(const ConstIterator &rhs) = default;
|
||||
ConstIterator &operator=(const ConstIterator &) = default;
|
||||
~ConstIterator() = default;
|
||||
ConstIterator &operator++() {
|
||||
++underlying;
|
||||
return *this;
|
||||
}
|
||||
ConstIterator operator++(int) {
|
||||
auto copy = *this;
|
||||
this->operator++();
|
||||
return copy;
|
||||
}
|
||||
ConstIterator &operator--() {
|
||||
--underlying;
|
||||
return *this;
|
||||
}
|
||||
ConstIterator operator--(int) {
|
||||
auto copy = *this;
|
||||
this->operator--();
|
||||
return copy;
|
||||
}
|
||||
const ValueType &operator*() const { return this->underlying.operator*(); }
|
||||
const ValueType *operator->() const { return &this->operator*(); }
|
||||
|
||||
friend bool operator==(const ConstIterator &lhs, const ConstIterator &rhs) {
|
||||
return lhs.underlying == rhs.underlying;
|
||||
}
|
||||
friend bool operator!=(const ConstIterator &lhs, const ConstIterator &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
friend bool operator==(const Iterator &lhs, const ConstIterator &rhs) {
|
||||
return lhs == rhs.underlying;
|
||||
}
|
||||
friend bool operator!=(const Iterator &lhs, const ConstIterator &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
friend bool operator==(const ConstIterator &lhs, const Iterator &rhs) {
|
||||
return lhs.underlying == rhs;
|
||||
}
|
||||
friend bool operator!=(const ConstIterator &lhs, const Iterator &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
class ReverseIterator {
|
||||
Iterator underlying;
|
||||
ReverseIterator(const Iterator &underlying) : underlying{underlying} {}
|
||||
|
||||
public:
|
||||
friend Map;
|
||||
ReverseIterator() = delete;
|
||||
ReverseIterator(const ReverseIterator &) = default;
|
||||
~ReverseIterator() = default;
|
||||
ReverseIterator &operator=(const ReverseIterator &) = default;
|
||||
ReverseIterator &operator++() {
|
||||
--underlying;
|
||||
return *this;
|
||||
}
|
||||
ReverseIterator operator++(int) {
|
||||
auto copy = *this;
|
||||
this->operator++();
|
||||
return copy;
|
||||
}
|
||||
ReverseIterator &operator--() {
|
||||
++underlying;
|
||||
return *this;
|
||||
}
|
||||
ReverseIterator operator--(int) {
|
||||
auto copy = *this;
|
||||
this->operator++();
|
||||
return copy;
|
||||
}
|
||||
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}, 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();
|
||||
}
|
||||
while (max->right) {
|
||||
max = max->right.get();
|
||||
}
|
||||
}
|
||||
Map(Map &&rhs) : root{std::move(rhs.root)}, _size{rhs._size} {
|
||||
this->min = this->root.get();
|
||||
this->max = this->root.get();
|
||||
while (min->left) {
|
||||
min = min->left.get();
|
||||
}
|
||||
while (max->right) {
|
||||
max = max->right.get();
|
||||
}
|
||||
}
|
||||
Map() : root{}, _size{0} {}
|
||||
Map(const Map &rhs) : root{rhs.root}, _size{rhs._size} {}
|
||||
Map(Map &&rhs) : root{std::move(rhs.root)}, _size{rhs._size} {}
|
||||
Map &operator=(const Map &rhs) {
|
||||
this->root = std::make_unique<Node>(*rhs.root);
|
||||
this->root = rhs.root;
|
||||
this->_size = rhs._size;
|
||||
this->min = this->root.get();
|
||||
this->max = this->root.get();
|
||||
while (min->left) {
|
||||
min = min->left.get();
|
||||
}
|
||||
while (max->right) {
|
||||
max = max->right.get();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Map &operator=(Map &&rhs) {
|
||||
this->root = std::move(rhs.root);
|
||||
this->_size = rhs._size;
|
||||
this->min = this->root.get();
|
||||
this->max = this->root.get();
|
||||
while (min->left) {
|
||||
min = min->left.get();
|
||||
}
|
||||
while (max->right) {
|
||||
max = max->right.get();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Map(std::initializer_list<std::pair<const Key_T, Mapped_T>> items) : Map{} {
|
||||
this->insert(items.begin(), items.end());
|
||||
}
|
||||
std::size_t size() const { return this->_size; }
|
||||
bool empty() const { return this->size() == 0; }
|
||||
std::size_t size() { return this->_size; }
|
||||
|
||||
private:
|
||||
// private helpers
|
||||
void rotate_root(Direction dir) {
|
||||
assert(root);
|
||||
assert(root.has_value());
|
||||
|
||||
std::unique_ptr<Node> new_root = root->uchild(!dir);
|
||||
std::unique_ptr<Node> new_root = root.value().uchild(!dir);
|
||||
// can't make null the new root
|
||||
assert(new_root);
|
||||
|
||||
std::unique_ptr<Node> old_root = std::move(root);
|
||||
std::unique_ptr<Node> old_root =
|
||||
std::make_unique<Node>(std::move(root.value()));
|
||||
|
||||
root = std::move(new_root);
|
||||
root->parent = nullptr;
|
||||
root.value() = std::move(*new_root);
|
||||
|
||||
old_root->set_child(!dir, root->uchild(dir));
|
||||
|
||||
if (old_root->left) {
|
||||
old_root->left->parent = old_root.get();
|
||||
}
|
||||
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();
|
||||
// }
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
old_root->set_child(!dir, root.value().uchild(dir));
|
||||
root.value().set_child(dir, std::move(old_root));
|
||||
}
|
||||
|
||||
template <bool trace = false>
|
||||
std::pair<Node const *, Direction> locate(const Key_T &key) const {
|
||||
Node const *ret_parent;
|
||||
std::pair<Node *, Direction> locate(const Key_T &key) {
|
||||
Node *ret_parent;
|
||||
Direction ret_dir;
|
||||
// map is empty
|
||||
if (!this->root) {
|
||||
if (!this->root.has_value()) {
|
||||
if constexpr (trace) {
|
||||
std::cerr << "(map empty)" << std::endl;
|
||||
}
|
||||
return std::make_pair(nullptr, ret_dir);
|
||||
}
|
||||
if constexpr (trace) {
|
||||
std::cerr << "root";
|
||||
}
|
||||
// value is in root
|
||||
if (this->root->val->first == key) {
|
||||
if (this->root.value().val.first == key) {
|
||||
|
||||
if constexpr (trace) {
|
||||
std::cerr << "->found" << std::endl;
|
||||
}
|
||||
|
||||
return std::make_pair(nullptr, ret_dir);
|
||||
}
|
||||
ret_parent = this->root.get();
|
||||
if (key < ret_parent->val->first) {
|
||||
ret_parent = &this->root.value();
|
||||
if (key < ret_parent->val.first) {
|
||||
if constexpr (trace) {
|
||||
std::cerr << "->left";
|
||||
}
|
||||
ret_dir = Direction::Left;
|
||||
} else {
|
||||
if constexpr (trace) {
|
||||
std::cerr << "->right";
|
||||
}
|
||||
ret_dir = Direction::Right;
|
||||
}
|
||||
while (ret_parent->child(ret_dir) &&
|
||||
!(ret_parent->child(ret_dir)->val->first == key)) {
|
||||
ret_parent->child(ret_dir)->val.first != key) {
|
||||
ret_parent = ret_parent->child(ret_dir);
|
||||
if (key < ret_parent->val->first) {
|
||||
if (key < ret_parent->val.first) {
|
||||
if constexpr (trace) {
|
||||
std::cerr << "->left";
|
||||
}
|
||||
ret_dir = Direction::Left;
|
||||
} else {
|
||||
if constexpr (trace) {
|
||||
std::cerr << "->right";
|
||||
}
|
||||
ret_dir = Direction::Right;
|
||||
}
|
||||
}
|
||||
if constexpr (trace) {
|
||||
std::cerr << "->found" << std::endl;
|
||||
}
|
||||
return std::make_pair(ret_parent, ret_dir);
|
||||
}
|
||||
void hard_erase(Node *n) {
|
||||
|
@ -700,7 +466,7 @@ private:
|
|||
// bookkeeping
|
||||
redcheck(sibling_color) {
|
||||
// case 3
|
||||
if (parent->parent) {
|
||||
if (parent->parent != nullptr) {
|
||||
parent->rotate(dir);
|
||||
} else {
|
||||
parent->map->rotate_root(dir);
|
||||
|
@ -737,7 +503,7 @@ private:
|
|||
assert(parent);
|
||||
assert(sibling);
|
||||
assert(distant);
|
||||
if (parent->parent) {
|
||||
if (parent->parent != nullptr) {
|
||||
parent->rotate(dir);
|
||||
} else {
|
||||
parent->map->rotate_root(dir);
|
||||
|
@ -766,38 +532,35 @@ private:
|
|||
}
|
||||
}
|
||||
bool core_erase(Node *erasing) {
|
||||
Color c = erasing->color;
|
||||
// 2 children
|
||||
if (erasing->left && erasing->right) {
|
||||
Node *succ = erasing->next;
|
||||
erasing->val = std::move(succ->val);
|
||||
erasing->val = succ->val;
|
||||
this->core_erase(succ);
|
||||
}
|
||||
// 1 child
|
||||
else if (erasing->left) {
|
||||
*erasing = std::move(*erasing->left.release());
|
||||
*erasing = std::move(*erasing->left);
|
||||
if (erasing->prev != nullptr) {
|
||||
erasing->prev->next = erasing;
|
||||
}
|
||||
if (erasing->next != nullptr) {
|
||||
erasing->next->prev = erasing;
|
||||
}
|
||||
erasing->color = c;
|
||||
return true;
|
||||
} else if (erasing->right) {
|
||||
*erasing = std::move(*erasing->right.release());
|
||||
*erasing = std::move(*erasing->right);
|
||||
if (erasing->prev != nullptr) {
|
||||
erasing->prev->next = erasing;
|
||||
}
|
||||
if (erasing->next != nullptr) {
|
||||
erasing->next->prev = erasing;
|
||||
}
|
||||
erasing->color = c;
|
||||
return true;
|
||||
}
|
||||
// no children and root
|
||||
else if (erasing->parent == nullptr) {
|
||||
erasing->map->root = nullptr;
|
||||
else if (!erasing->parent) {
|
||||
erasing->map->root = std::nullopt;
|
||||
}
|
||||
// no children and red
|
||||
else if (erasing->color == Color::Red) {
|
||||
|
@ -815,30 +578,30 @@ public:
|
|||
Iterator find(const Key_T &key) {
|
||||
auto [parent, dir] = locate(key);
|
||||
if (parent == nullptr) {
|
||||
if (this->root) {
|
||||
if (this->root->val->first == key) {
|
||||
return Iterator{this->root.get()};
|
||||
if (this->root.has_value()) {
|
||||
if (this->root.value().val.first == key) {
|
||||
return Iterator{&this->root.value()};
|
||||
}
|
||||
}
|
||||
return this->end();
|
||||
}
|
||||
if (parent->child(dir) != nullptr) {
|
||||
return Iterator{const_cast<Node *>(parent->child(dir)), nullptr};
|
||||
return Iterator{parent->child(dir), nullptr};
|
||||
}
|
||||
return this->end();
|
||||
}
|
||||
ConstIterator find(const Key_T &key) const {
|
||||
auto [parent, dir] = locate(key);
|
||||
Iterator find_trace(const Key_T &key) {
|
||||
auto [parent, dir] = locate<true>(key);
|
||||
if (parent == nullptr) {
|
||||
if (this->root) {
|
||||
if (this->root->val->first == key) {
|
||||
return Iterator{const_cast<Node *>(this->root.get())};
|
||||
if (this->root.has_value()) {
|
||||
if (this->root.value().val.first == key) {
|
||||
return Iterator{&this->root.value()};
|
||||
}
|
||||
}
|
||||
return this->end();
|
||||
}
|
||||
if (parent->child(dir) != nullptr) {
|
||||
return Iterator{const_cast<Node *>(parent->child(dir)), nullptr};
|
||||
return Iterator{parent->child(dir), nullptr};
|
||||
}
|
||||
return this->end();
|
||||
}
|
||||
|
@ -850,71 +613,45 @@ public:
|
|||
auto [parent, dir] = locate(key);
|
||||
// located root node
|
||||
if (parent == nullptr) {
|
||||
if (this->root) {
|
||||
return std::make_pair(Iterator{root.get()}, false);
|
||||
if (this->root.has_value()) {
|
||||
return std::make_pair(Iterator{&root.value()}, false);
|
||||
} else {
|
||||
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.get()}, true);
|
||||
this->root = Node{val, this};
|
||||
return std::make_pair(Iterator{&root.value()}, true);
|
||||
}
|
||||
}
|
||||
|
||||
// non-root node
|
||||
if (parent->child(dir)) {
|
||||
// node already present
|
||||
return std::make_pair(Iterator{const_cast<Node *>(parent->child(dir))},
|
||||
false);
|
||||
return std::make_pair(Iterator{parent->child(dir)}, false);
|
||||
}
|
||||
|
||||
// need to insert non-root node
|
||||
Map *m = const_cast<Map *>(this);
|
||||
Node *new_node = const_cast<Node *>(parent)
|
||||
->set_child(dir, std::make_unique<Node>(Node{val, m}))
|
||||
.get();
|
||||
Node *new_node =
|
||||
parent->set_child(dir, std::make_unique<Node>(Node{val, this})).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;
|
||||
}
|
||||
if (this->max == parent && dir == Direction::Right) {
|
||||
this->max = new_node;
|
||||
}
|
||||
this->_size++;
|
||||
return std::make_pair(Iterator{new_node}, true);
|
||||
}
|
||||
void erase(Iterator pos) {
|
||||
if (pos.underlying == nullptr) {
|
||||
return;
|
||||
}
|
||||
this->_size--;
|
||||
|
||||
Node *before = pos.underlying->prev;
|
||||
Node *after = pos.underlying->next;
|
||||
|
||||
if (core_erase(pos.underlying)) {
|
||||
pos.underlying->restore_ordering();
|
||||
} else {
|
||||
if (before != nullptr && before->valid == 0x13371337) {
|
||||
if (before != nullptr) {
|
||||
before->next = before->calc_succ();
|
||||
if (before->next != nullptr) {
|
||||
before->next->prev = before;
|
||||
}
|
||||
} else {
|
||||
this->min = after;
|
||||
}
|
||||
if (after != nullptr && after->valid == 0x13371337) {
|
||||
if (after != nullptr) {
|
||||
after->prev = after->calc_pred();
|
||||
if (after->prev != nullptr) {
|
||||
after->prev->next = after;
|
||||
}
|
||||
} else {
|
||||
this->max = before;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -922,75 +659,6 @@ public:
|
|||
// baseline iterator creation
|
||||
Iterator begin() { return Iterator{min, nullptr}; }
|
||||
Iterator end() { return Iterator{nullptr, max}; }
|
||||
ConstIterator begin() const { return Iterator{min, nullptr}; }
|
||||
ConstIterator end() const { return Iterator{nullptr, max}; }
|
||||
ReverseIterator rbegin() { return Iterator{max, nullptr}; }
|
||||
ReverseIterator rend() { return Iterator{nullptr, min}; }
|
||||
|
||||
// misc that can be implemented with the above or trivially
|
||||
void clear() {
|
||||
this->root = std::move(nullptr);
|
||||
this->_size = 0;
|
||||
}
|
||||
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) {
|
||||
this->insert({key, {}});
|
||||
return this->at(key);
|
||||
}
|
||||
template <typename IT_T> void insert(IT_T range_beg, IT_T range_end) {
|
||||
while (range_beg != range_end) {
|
||||
auto [first, second] = *range_beg;
|
||||
this->insert(std::make_pair(first, second));
|
||||
++range_beg;
|
||||
}
|
||||
}
|
||||
void erase(const Key_T &key) { this->erase(this->find(key)); }
|
||||
friend bool operator==(const Map &lhs, const Map &rhs) {
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
auto lhs_iter = lhs.begin();
|
||||
auto rhs_iter = rhs.begin();
|
||||
while (lhs_iter != lhs.end()) {
|
||||
if (*lhs_iter != *rhs_iter) {
|
||||
return false;
|
||||
}
|
||||
++lhs_iter;
|
||||
++rhs_iter;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
friend bool operator!=(const Map &lhs, const Map &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
friend bool operator<(const Map &lhs, const Map &rhs) {
|
||||
auto lhs_iter = lhs.begin();
|
||||
auto rhs_iter = rhs.begin();
|
||||
while (lhs_iter != lhs.end() && rhs_iter != rhs.end()) {
|
||||
if (*lhs_iter < *rhs_iter) {
|
||||
return true;
|
||||
}
|
||||
if (*lhs_iter != *rhs_iter) {
|
||||
return false;
|
||||
}
|
||||
++lhs_iter;
|
||||
++rhs_iter;
|
||||
}
|
||||
return lhs.size() < rhs.size();
|
||||
}
|
||||
};
|
||||
} // namespace cs440
|
||||
#endif
|
||||
|
|
119
minimal.cpp
119
minimal.cpp
|
@ -1,119 +0,0 @@
|
|||
#include "Map.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
// basically an int wrapper
|
||||
class MyKeyType {
|
||||
private:
|
||||
int val;
|
||||
|
||||
public:
|
||||
// not default constructable, not copy assignable, not move assignable
|
||||
MyKeyType() = delete;
|
||||
MyKeyType &operator=(const MyKeyType &) = delete;
|
||||
MyKeyType &operator=(MyKeyType &&) = delete;
|
||||
|
||||
// copy constructable and move assignable
|
||||
MyKeyType(MyKeyType &&) = default;
|
||||
MyKeyType(const MyKeyType &) = default;
|
||||
~MyKeyType() = default;
|
||||
|
||||
MyKeyType(int i) : val(i) {}
|
||||
|
||||
bool operator<(const MyKeyType &other) const { return this->val < other.val; }
|
||||
|
||||
bool operator==(const MyKeyType &other) const {
|
||||
return this->val == other.val;
|
||||
}
|
||||
};
|
||||
|
||||
// same as keytype except no operator<
|
||||
class MyValueType {
|
||||
private:
|
||||
int val;
|
||||
|
||||
public:
|
||||
// not default constructable, not copy assignable, not move assignable
|
||||
MyValueType() = delete;
|
||||
MyValueType &operator=(const MyValueType &) = delete;
|
||||
MyValueType &operator=(MyValueType &&) = delete;
|
||||
|
||||
// copy constructable and move assignable
|
||||
MyValueType(MyValueType &&) = default;
|
||||
MyValueType(const MyValueType &) = default;
|
||||
~MyValueType() = default;
|
||||
|
||||
MyValueType(int i) : val(i) {}
|
||||
|
||||
bool operator==(const MyValueType &other) const {
|
||||
return this->val == other.val;
|
||||
}
|
||||
};
|
||||
|
||||
class MyDefaultConstructible {
|
||||
|
||||
friend bool operator<(const MyDefaultConstructible &o1,
|
||||
const MyDefaultConstructible &o2) {
|
||||
return o1.val < o2.val;
|
||||
}
|
||||
|
||||
private:
|
||||
int val = 0;
|
||||
|
||||
public:
|
||||
// not copy assignable, not move assignable
|
||||
MyDefaultConstructible &operator=(const MyDefaultConstructible &) = delete;
|
||||
MyDefaultConstructible &operator=(MyDefaultConstructible &&) = delete;
|
||||
|
||||
// default constructable, copy constructable and move assignable
|
||||
MyDefaultConstructible() = default;
|
||||
MyDefaultConstructible(MyDefaultConstructible &&) = default;
|
||||
MyDefaultConstructible(const MyDefaultConstructible &) = default;
|
||||
~MyDefaultConstructible() = default;
|
||||
|
||||
MyDefaultConstructible(int i) : val(i) {}
|
||||
|
||||
bool operator==(const MyDefaultConstructible &other) const {
|
||||
return this->val == other.val;
|
||||
}
|
||||
};
|
||||
|
||||
class MyAssignable {
|
||||
private:
|
||||
int val = 0;
|
||||
|
||||
public:
|
||||
MyAssignable() = default;
|
||||
MyAssignable(int i) : val(i) {}
|
||||
bool operator==(const MyAssignable &other) const {
|
||||
return this->val == other.val;
|
||||
}
|
||||
};
|
||||
|
||||
// manual instantiation, instantiates every member function instead of
|
||||
// just the ones called
|
||||
template class cs440::Map<MyKeyType, MyDefaultConstructible>;
|
||||
|
||||
int main() {
|
||||
cs440::Map<MyKeyType, MyValueType> m{{3, 5}};
|
||||
m.insert({{2}, {3}});
|
||||
m.insert({{1}, {3}});
|
||||
m.insert({{5}, {3}});
|
||||
m.insert({{7}, {3}});
|
||||
m.at(2);
|
||||
auto iter = m.find(2);
|
||||
m.erase(iter);
|
||||
auto m_copy = m;
|
||||
assert(m_copy == m);
|
||||
|
||||
cs440::Map<MyKeyType, MyDefaultConstructible> m2{{8, 9}};
|
||||
m2[10]; // should default construct these values
|
||||
m2[15];
|
||||
|
||||
cs440::Map<MyKeyType, MyAssignable> m3{{6, 7}};
|
||||
m3[20] = {5}; // move assign
|
||||
MyAssignable ma{1};
|
||||
m3[10] = ma; // copy assign
|
||||
|
||||
return 0;
|
||||
}
|
67
morseex.cpp
67
morseex.cpp
|
@ -1,67 +0,0 @@
|
|||
#include "Map.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <cctype>
|
||||
|
||||
|
||||
cs440::Map<char, std::string> morse {
|
||||
{',', "--..--"},
|
||||
{'.', ".-.-.-"},
|
||||
{'?', "..--.."},
|
||||
{'0', "----- "},
|
||||
{'1', ".---- "},
|
||||
{'2', "..--- "},
|
||||
{'3', "...-- "},
|
||||
{'4', "....- "},
|
||||
{'5', "..... "},
|
||||
{'6', "-.... "},
|
||||
{'7', "--... "},
|
||||
{'8', "---.. "},
|
||||
{'9', "----. "},
|
||||
{'A', ".-"},
|
||||
{'B', "-..."},
|
||||
{'C', "-.-."},
|
||||
{'D', "-.. "},
|
||||
{'E', "."},
|
||||
{'F', "..-."},
|
||||
{'G', "--."},
|
||||
{'H', "...."},
|
||||
{'I', ".."},
|
||||
{'J', ".---"},
|
||||
{'K', "-.-"},
|
||||
{'L', ".-.."},
|
||||
{'M', "--"},
|
||||
{'N', "-."},
|
||||
{'O', "---"},
|
||||
{'P', ".--."},
|
||||
{'Q', "--.-"},
|
||||
{'R', ".-."},
|
||||
{'S', "..."},
|
||||
{'T', "-"},
|
||||
{'U', "..-"},
|
||||
{'V', "...-"},
|
||||
{'W', ".--"},
|
||||
{'X', "-..-"},
|
||||
{'Y', "-.--"},
|
||||
{'Z', "--.."},
|
||||
};
|
||||
|
||||
|
||||
int main() {
|
||||
std::cout << "Enter messages to be translated (CTRL+D to exit)\n";
|
||||
|
||||
std::string message;
|
||||
while(std::cin >> message) {
|
||||
for (auto c : message) {
|
||||
try {
|
||||
std::cout << morse.at(toupper(c)) << '\n';
|
||||
} catch (std::out_of_range) {
|
||||
std::cout << "invalid character: " << c << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
14
suspects
14
suspects
|
@ -1 +1,13 @@
|
|||
rotation is definitely wrong somewhere, knowing me rotate_root, somehow 1 ended up right of 2
|
||||
suspects:
|
||||
|
||||
core_erase
|
||||
|
||||
erase_child
|
||||
|
||||
hard_erase
|
||||
|
||||
rotate
|
||||
|
||||
Soft okay:
|
||||
|
||||
assignment operator for node specifically dealing with the parent pointer if we assign to something and then don't immediately set the parent appropriately bad things happen, just setting to rhs didn't fix but it also didn't break anything so it's soft okay
|
||||
|
|
30
t.cpp
30
t.cpp
|
@ -1,20 +1,22 @@
|
|||
#include "Map.hpp"
|
||||
#include <utility>
|
||||
|
||||
template class cs440::Map<int, int>;
|
||||
int main(void) {
|
||||
// cs440::Map<int, int> a;
|
||||
// for (std::size_t i = 10; i >= 1; i--) {
|
||||
// a.insert({i, i});
|
||||
// for (std::size_t j = 10; j >= i; j--) {
|
||||
// }
|
||||
// }
|
||||
// for (std::size_t i = 10; i >= 5; i--) {
|
||||
// std::cout << i << std::endl;
|
||||
// auto b = a.find(i);
|
||||
// a.erase(b);
|
||||
// for (std::size_t j = 1; j <= i; j++) {
|
||||
// }
|
||||
// }
|
||||
cs440::Map<int, int> a;
|
||||
a.insert({1, 1});
|
||||
a.insert({2, 2});
|
||||
a.insert({3, 3});
|
||||
a.insert({4, 4});
|
||||
a.insert({5, 5});
|
||||
a.insert({6, 6});
|
||||
a.insert({7, 7});
|
||||
a.insert({8, 8});
|
||||
a.insert({9, 9});
|
||||
a.insert({10, 10});
|
||||
for (std::size_t i = 1; i <= 10; i++) {
|
||||
// a.find(i).check();
|
||||
std::cout << i << std::endl;
|
||||
a.erase(a.find(i));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
35
ta.cpp
35
ta.cpp
|
@ -1,35 +0,0 @@
|
|||
#include "Map.hpp"
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
template class cs440::Map<int, int>;
|
||||
int main(void) {
|
||||
cs440::Map<std::string, int> a;
|
||||
std::stringstream ss;
|
||||
std::string s;
|
||||
for (std::size_t i = 10; i >= 1; i--) {
|
||||
ss << i;
|
||||
ss >> s;
|
||||
a.insert({s, i});
|
||||
for (std::size_t j = 10; j >= i; j--) {
|
||||
ss << j;
|
||||
ss >> s;
|
||||
}
|
||||
}
|
||||
for (std::size_t i = 1; i <= 10; i++) {
|
||||
ss << i;
|
||||
ss >> s;
|
||||
}
|
||||
for (std::size_t i = 10; i >= 5; i--) {
|
||||
std::cout << i << std::endl;
|
||||
ss << i;
|
||||
ss >> s;
|
||||
auto b = a.find(s);
|
||||
a.erase(b);
|
||||
for (std::size_t j = 1; j <= i; j++) {
|
||||
ss << j;
|
||||
ss >> s;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
620
test-kec.cpp
620
test-kec.cpp
|
@ -1,620 +0,0 @@
|
|||
/*
|
||||
* Run with
|
||||
*
|
||||
* -i iterations
|
||||
*
|
||||
* to do a stress test for the given number of iterations.
|
||||
*
|
||||
* -p
|
||||
*
|
||||
* to print correct output.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include "Map.hpp"
|
||||
|
||||
/*
|
||||
* Wrapper class around std::map to handle slight difference in return value and also
|
||||
* provide an Iterator nested name.
|
||||
*/
|
||||
|
||||
template <typename K, typename V>
|
||||
class test_map : public std::map<K, V> {
|
||||
private:
|
||||
using base_t = std::map<K, V>;
|
||||
public:
|
||||
using Iterator = typename base_t::iterator;
|
||||
std::pair<typename base_t::iterator, bool>insert(const std::pair<const K, V> &p) {
|
||||
return this->base_t::insert(p);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Person class.
|
||||
*/
|
||||
|
||||
struct Person {
|
||||
friend bool operator<(const Person &p1, const Person &p2) {
|
||||
return p1.name < p2.name;
|
||||
}
|
||||
friend bool operator==(const Person &p1, const Person &p2) {
|
||||
return p1.name == p2.name;
|
||||
}
|
||||
Person(const char *n) : name(n) {}
|
||||
void print() const {
|
||||
printf("Name: %s\n", name.c_str());
|
||||
}
|
||||
const std::string name;
|
||||
Person &operator=(const Person &) = delete;
|
||||
};
|
||||
|
||||
void
|
||||
print(const std::pair<const Person, int> &p) {
|
||||
p.first.print();
|
||||
printf(" %d\n", p.second);
|
||||
}
|
||||
|
||||
/*
|
||||
* MyClass class.
|
||||
*/
|
||||
|
||||
struct MyClass {
|
||||
friend bool operator<(const MyClass &o1, const MyClass &o2) {
|
||||
return o1.num < o2.num;
|
||||
}
|
||||
friend bool operator==(const MyClass &o1, const MyClass &o2) {
|
||||
return o1.num == o2.num;
|
||||
}
|
||||
MyClass(double n) : num(n) {}
|
||||
double num;
|
||||
};
|
||||
|
||||
void
|
||||
print(const std::pair<const int, std::string> &p) {
|
||||
printf("%d, %s; ", p.first, p.second.c_str());
|
||||
}
|
||||
|
||||
/*
|
||||
* Stress class.
|
||||
*/
|
||||
|
||||
struct Stress {
|
||||
friend bool operator<(const Stress& o1, const Stress& o2) {
|
||||
return o1.val < o2.val;
|
||||
}
|
||||
friend bool operator==(const Stress& o1, const Stress& o2) {
|
||||
return o1.val == o2.val;
|
||||
}
|
||||
Stress(int _v) : val(_v){}
|
||||
int val;
|
||||
};
|
||||
// Helper function for stress testing. This orders iterators by what they point to.
|
||||
template <template <typename, typename> class MAP_T>
|
||||
inline bool
|
||||
less(const typename MAP_T<const Stress, double>::Iterator &lhs, const typename MAP_T<const Stress, double>::Iterator &rhs) {
|
||||
return (*lhs).first.val < (*rhs).first.val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Additional test functions for BST.
|
||||
*/
|
||||
|
||||
template <template <typename, typename> class MAP_T>
|
||||
void traverse(const MAP_T<const Person, int> &, int level);
|
||||
|
||||
template <template <typename, typename> class MAP_T>
|
||||
void traverse2(int level);
|
||||
|
||||
template <template <typename, typename> class MAP_T>
|
||||
void check(const MAP_T<const Stress, double> &, const std::map<const Stress, double> &);
|
||||
|
||||
/*
|
||||
* The actual test code. It's a template so that it can be run with the std::map and the
|
||||
* assignment Map.
|
||||
*/
|
||||
|
||||
template <template <typename, typename> class MAP_T>
|
||||
void
|
||||
run_test(int iterations) {
|
||||
|
||||
/*
|
||||
* Test with Person.
|
||||
*/
|
||||
|
||||
{
|
||||
Person p1("Jane");
|
||||
Person p2("John");
|
||||
Person p3("Mary");
|
||||
Person p4("Dave");
|
||||
|
||||
MAP_T<const Person, int> map;
|
||||
|
||||
// Insert people into the map.
|
||||
auto p1_it = map.insert(std::make_pair(p1, 1));
|
||||
map.insert(std::make_pair(p2, 2));
|
||||
map.insert(std::make_pair(p3, 3));
|
||||
map.insert(std::make_pair(p4, 4));
|
||||
|
||||
// Check iterator equality.
|
||||
{
|
||||
// Returns an iterator pointing to the first element.
|
||||
auto it1 = map.begin();
|
||||
// Returns an iterator pointing to one PAST the last element. This
|
||||
// iterator is obviously conceptual only. It cannot be
|
||||
// dereferenced.
|
||||
auto it2 = map.end();
|
||||
|
||||
it1++; // Second node now.
|
||||
it1++; // Third node now.
|
||||
it2--; // Fourth node now.
|
||||
it2--; // Third node now.
|
||||
assert(it1 == it2);
|
||||
it2--; // Second node now.
|
||||
it2--; // First node now.
|
||||
assert(map.begin() == it2);
|
||||
}
|
||||
|
||||
// Check insert return value.
|
||||
{
|
||||
printf("---- Test insert() return.\n");
|
||||
// Insert returns an interator. If it's already in, it returns an
|
||||
// iterator to the already inserted element.
|
||||
auto it = map.insert(std::make_pair(p1, 1));
|
||||
assert(it.first == p1_it.first);
|
||||
// Now insert one that is new.
|
||||
it = map.insert(std::make_pair(Person("Larry"), 5));
|
||||
print(*(it.first));
|
||||
map.erase(it.first);
|
||||
}
|
||||
|
||||
// Print the whole thing now, to verify ordering.
|
||||
printf("---- Before erasures.\n");
|
||||
|
||||
// Iterate through the whole map, and call print() on each Person.
|
||||
for (auto &e : map) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
// Test multiple traversals of the same map.
|
||||
printf("---- Multiple traversals\n");
|
||||
traverse(map, 4);
|
||||
|
||||
// Test multiple BST at the same time.
|
||||
printf("---- Multiple BST\n");
|
||||
traverse2<MAP_T>(4);
|
||||
|
||||
/*
|
||||
* Test some erasures.
|
||||
*/
|
||||
|
||||
// Erase first element.
|
||||
map.erase(map.begin());
|
||||
auto it = map.end();
|
||||
--it; // it now points to last element.
|
||||
it--; // it now points to penultimate element.
|
||||
map.erase(it);
|
||||
|
||||
printf("---- After erasures.\n");
|
||||
|
||||
// Iterate through the whole map, and call print() on each Person.
|
||||
for (auto &e : map) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
// Test iterator validity.
|
||||
{
|
||||
// Iterators must be valid even when other things are inserted or
|
||||
// erased.
|
||||
printf("---- Test iterator non-invalidation\n");
|
||||
|
||||
// Get iterator to the first.
|
||||
auto b = map.begin();
|
||||
|
||||
// Insert element which will be at the end.
|
||||
auto it = map.insert(std::make_pair(Person("Zeke"), 10));
|
||||
|
||||
// Iterator to the first should still be valid.
|
||||
print(*b);
|
||||
|
||||
// Delete first, saving the actual object.
|
||||
auto tmp(*b); // Save, so we can reinsert.
|
||||
map.erase(map.begin()); // Erase it.
|
||||
|
||||
// Check iterator for inserted. Iterator to end should still be valid.
|
||||
print(*it.first); // This should still be valid.
|
||||
|
||||
// Reinsert first element.
|
||||
map.insert(tmp);
|
||||
|
||||
// Erase inserted last element.
|
||||
map.erase(it.first);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test Map with MyClass.
|
||||
*/
|
||||
|
||||
{
|
||||
MAP_T<const MyClass, std::string> map;
|
||||
|
||||
// Empty container, should print nothing.
|
||||
for (auto it = map.begin(); it != map.end(); ++it) {
|
||||
abort();
|
||||
}
|
||||
|
||||
MyClass m1(0), m2(3), m3(1), m4(2);
|
||||
auto m1_it = map.insert(std::make_pair(m1, "mmm1"));
|
||||
map.insert(std::make_pair(m2, "mmm2"));
|
||||
map.insert(std::make_pair(m3, "mmm3"));
|
||||
map.insert(std::make_pair(m4, "mmm4"));
|
||||
|
||||
// Should print 0.0 1.0 2.0 3.0
|
||||
for (auto &e : map) {
|
||||
printf("%3.1f ", e.first.num);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Check return value of insert.
|
||||
{
|
||||
// Already in, so must return equal to m1_it.
|
||||
auto it = map.insert(std::make_pair(m1, "mmm1"));
|
||||
assert(it.first == m1_it.first);
|
||||
}
|
||||
|
||||
// Erase the first element.
|
||||
map.erase(map.begin());
|
||||
// Should print "1.0 2.0 3.0".
|
||||
for (auto &e : map) {
|
||||
printf("%3.1f ", e.first.num);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Erase the new first element.
|
||||
map.erase(map.begin());
|
||||
// Should print "2.0 3.0".
|
||||
for (auto &e : map) {
|
||||
printf("%3.1f ", e.first.num);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
map.erase(--map.end());
|
||||
// Should print "2.0".
|
||||
for (auto &e : map) {
|
||||
printf("%3.1f ", e.first.num);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Erase the last element.
|
||||
map.erase(map.begin());
|
||||
// Should print nothing.
|
||||
for (auto &e : map) {
|
||||
printf("%3.1f ", e.first.num);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Test Map with plain int.
|
||||
*/
|
||||
|
||||
{
|
||||
MAP_T<const int, std::string> map;
|
||||
|
||||
// Empty container, should print nothing.
|
||||
for (auto &e : map) {
|
||||
printf("%d ", e.first);
|
||||
}
|
||||
|
||||
auto p1(std::make_pair(4, "444"));
|
||||
auto p2(std::make_pair(3, "333"));
|
||||
auto p3(std::make_pair(0, "000"));
|
||||
auto p4(std::make_pair(2, "222"));
|
||||
auto p5(std::make_pair(1, "111"));
|
||||
|
||||
map.insert(p1);
|
||||
map.insert(p2);
|
||||
map.insert(p3);
|
||||
map.insert(p4);
|
||||
map.insert(p5);
|
||||
|
||||
// Should print "0 1 2 3 4".
|
||||
for (auto it = map.begin(); it != map.end(); ++it) {
|
||||
print(*it);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Insert dupes.
|
||||
map.insert(p4);
|
||||
map.insert(p1);
|
||||
map.insert(p3);
|
||||
map.insert(p2);
|
||||
map.insert(p5);
|
||||
// Should print "0 1 2 3 4".
|
||||
for (auto it = map.begin(); it != map.end(); ++it) {
|
||||
print(*it);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Erase the first element.
|
||||
map.erase(map.begin());
|
||||
|
||||
// Erase the new first element.
|
||||
map.erase(map.begin());
|
||||
|
||||
// Erase the element in the end.
|
||||
map.erase(--map.end());
|
||||
// Should print "2 3".
|
||||
for (auto &e : map) {
|
||||
print(e);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Erase all elements.
|
||||
map.erase(map.begin());
|
||||
map.erase(map.begin());
|
||||
// Should print nothing.
|
||||
for (auto &e : map) {
|
||||
print(e);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Stress test Map.
|
||||
*/
|
||||
|
||||
if (iterations > 0) {
|
||||
|
||||
MAP_T<const Stress, double> map;
|
||||
using it_t = typename MAP_T<const Stress, double>::Iterator;
|
||||
using mirror_t = std::map<const Stress, double>;
|
||||
mirror_t mirror;
|
||||
|
||||
using iters_t = std::set<it_t, bool(*)(const it_t &lhs, const it_t &rhs)>;
|
||||
iters_t iters(&less<MAP_T>);
|
||||
|
||||
std::cout << "---- Starting stress test:" << std::endl;
|
||||
|
||||
const int N = iterations;
|
||||
|
||||
srand(9757);
|
||||
int n_inserted = 0, n_erased = 0, n_iters_changed = 0, n_empty = 0, n_dupes = 0;
|
||||
double avg_size = 0;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
|
||||
double op = drand48();
|
||||
|
||||
// The probability of removal should be slightly higher than the
|
||||
// probability of insertion so that the map is often empty.
|
||||
if (op < .44) {
|
||||
|
||||
// Insert an element. Repeat until no duplicate.
|
||||
do {
|
||||
// Limit the range of values of Stress so that we get some dupes.
|
||||
auto v(std::make_pair(Stress(rand()%50000), drand48()));
|
||||
auto find_it = map.find(v.first);
|
||||
auto it = map.insert(v);
|
||||
auto mir_res = mirror.insert(v);
|
||||
if (mir_res.second) {
|
||||
// If insert into mirror succeeded, insert into the map
|
||||
// should also have succeeded. It should not have
|
||||
// found it before insert.
|
||||
assert(find_it == map.end());
|
||||
// Store the iterator.
|
||||
iters.insert(it.first);
|
||||
break;
|
||||
}
|
||||
// If insert into mirror did not succeed, insert into map
|
||||
// should also not have succeeded, in which case, we
|
||||
// generate another value to store. Also, find should have
|
||||
// found it, and insert should have returned same iterator.
|
||||
assert(find_it == it.first);
|
||||
n_dupes++;
|
||||
} while (true);
|
||||
|
||||
++n_inserted;
|
||||
|
||||
} else if (op < .90) {
|
||||
|
||||
// Erase an element.
|
||||
if (iters.size() != 0) {
|
||||
|
||||
// Pick a random index.
|
||||
int index = rand()%iters.size();
|
||||
typename iters_t::iterator iit = iters.begin();
|
||||
while(index--) {
|
||||
++iit;
|
||||
}
|
||||
|
||||
auto it = *iit;
|
||||
// The iterator should not be end()
|
||||
assert(it != map.end());
|
||||
|
||||
Stress s((*it).first);
|
||||
mirror.erase(s);
|
||||
iters.erase(iit);
|
||||
map.erase(it);
|
||||
|
||||
++n_erased;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Does either postfix or prefix inc/dec operation.
|
||||
auto either_or = [&](it_t &it, it_t &(it_t::*f1)(), it_t (it_t::*f2)(int)) {
|
||||
if (rand()%2 == 0) {
|
||||
(it.*f1)();
|
||||
} else {
|
||||
(it.*f2)(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Increment or decrement an iterator.
|
||||
|
||||
// Size of containers should be same
|
||||
assert(iters.size() == mirror.size());
|
||||
|
||||
// If the container is empty, skip
|
||||
if (iters.size() != 0) {
|
||||
|
||||
// Pick a random index
|
||||
int index = rand()%iters.size();
|
||||
typename iters_t::iterator iters_it = iters.begin();
|
||||
while (index--) {
|
||||
++iters_it;
|
||||
}
|
||||
|
||||
auto it = *iters_it;
|
||||
// The iterator should not be end().
|
||||
assert(it != map.end());
|
||||
|
||||
// If it is the begin(), then only increment,
|
||||
// otherwise, pick either forward or backward.
|
||||
if (it == map.begin()) {
|
||||
either_or(it, &it_t::operator++, &it_t::operator++);
|
||||
++iters_it;
|
||||
} else {
|
||||
if (rand()%2 == 0) {
|
||||
either_or(it, &it_t::operator++, &it_t::operator++);
|
||||
++iters_it;
|
||||
} else {
|
||||
either_or(it, &it_t::operator--, &it_t::operator--);
|
||||
--iters_it;
|
||||
}
|
||||
}
|
||||
// If we didn't hit the end, replace the resulting iterator
|
||||
// in the iterator list.
|
||||
// Note that the set is sorted.
|
||||
if (it != map.end()) {
|
||||
assert(it == *iters_it);
|
||||
iters.erase(iters_it);
|
||||
iters.insert(it);
|
||||
}
|
||||
}
|
||||
|
||||
++n_iters_changed;
|
||||
}
|
||||
|
||||
avg_size += double(iters.size())/N;
|
||||
|
||||
if (iters.size() == 0) {
|
||||
++n_empty;
|
||||
}
|
||||
|
||||
check(map, mirror);
|
||||
}
|
||||
|
||||
std::cout << "inserted: " << n_inserted << " times" << std::endl;
|
||||
std::cout << "erased: " << n_erased << " times" << std::endl;
|
||||
std::cout << "iterators changed: " << n_iters_changed << " times" << std::endl;
|
||||
std::cout << "empty count: " << n_empty << std::endl;
|
||||
std::cout << "avg size: " << avg_size << std::endl;
|
||||
std::cout << "n dupes: " << n_dupes << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Main.
|
||||
*/
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
|
||||
bool correct_output = false;
|
||||
int iterations = 0;
|
||||
|
||||
{
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "pi:")) != EOF) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
correct_output = true;
|
||||
break;
|
||||
case 'i':
|
||||
iterations = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "Unrecog.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srand48(1234);
|
||||
|
||||
if (correct_output) {
|
||||
run_test<test_map>(iterations);
|
||||
} else {
|
||||
run_test<cs440::Map>(iterations);
|
||||
}
|
||||
}
|
||||
|
||||
template <template <typename, typename> class MAP_T>
|
||||
void
|
||||
check(const MAP_T<const Stress, double> &map, const std::map<const Stress, double> &mirror) {
|
||||
|
||||
// Check if the reference container and stress container is identical
|
||||
auto it = map.begin();
|
||||
auto mit = mirror.begin();
|
||||
|
||||
for( ; it != map.end() && mit != mirror.end(); ++it, ++mit) {
|
||||
|
||||
if ((*it).first == (*mit).first) {
|
||||
if ((*it).second == (*mit).second) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Reference tree and test tree differ.\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
if (it != map.end() || mit != mirror.end()) {
|
||||
fprintf(stderr, "Reference tree and test tree differ.\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Test single list being traversed by multiple iterators simultaneously.
|
||||
template <template <typename, typename> class MAP_T>
|
||||
void
|
||||
traverse(const MAP_T<const Person, int> &m, int level) {
|
||||
for (auto it = m.begin(); it != m.end(); ++it) {
|
||||
print(*it);
|
||||
if (level != 0) {
|
||||
traverse(m, level - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test multiple lists and multiple iterators.
|
||||
template <template <typename, typename> class MAP_T>
|
||||
void
|
||||
traverse2(int level) {
|
||||
|
||||
MAP_T<const Person, int> map;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
char name[30];
|
||||
sprintf(name, "Jane%d", int(10000*drand48()));
|
||||
printf("Generated name: %s\n", name);
|
||||
map.insert(std::make_pair(Person(name), 10*level + i));
|
||||
}
|
||||
|
||||
for (auto &e : map) {
|
||||
print(e);
|
||||
if (level != 0) {
|
||||
traverse2<MAP_T>(level - 1);
|
||||
}
|
||||
}
|
||||
}
|
620
test-scaling.cpp
620
test-scaling.cpp
|
@ -1,620 +0,0 @@
|
|||
#include "Map.hpp"
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
#include <cxxabi.h>
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <initializer_list>
|
||||
#include <set>
|
||||
|
||||
//Enables iteration test on a map larger than the memory available to the remote cluster
|
||||
//WARNING: This will be VERY slow.
|
||||
#define DO_BIG_ITERATION_TEST 0
|
||||
|
||||
namespace cs440 {
|
||||
template <typename K, typename V>
|
||||
class StdMapWrapper {
|
||||
private:
|
||||
using base_map = std::map<K, V>;
|
||||
|
||||
public:
|
||||
typedef typename base_map::iterator Iterator;
|
||||
typedef typename base_map::const_iterator ConstIterator;
|
||||
typedef typename base_map::reverse_iterator ReverseIterator;
|
||||
typedef typename base_map::const_reverse_iterator ConstReverseIterator;
|
||||
typedef typename base_map::value_type value_type;
|
||||
typedef typename base_map::mapped_type mapped_type;
|
||||
typedef typename base_map::key_type key_type;
|
||||
|
||||
StdMapWrapper() {}
|
||||
StdMapWrapper(std::initializer_list<std::pair<K,V>> il) {
|
||||
for(auto x : il) {
|
||||
m_map.insert(x);
|
||||
}
|
||||
}
|
||||
|
||||
StdMapWrapper(StdMapWrapper &&other)
|
||||
: m_map(std::move(other.m_map))
|
||||
{}
|
||||
|
||||
StdMapWrapper(const StdMapWrapper &other)
|
||||
: m_map(other.m_map)
|
||||
{}
|
||||
|
||||
StdMapWrapper &operator=(const StdMapWrapper &other) {
|
||||
if(this != &other) {
|
||||
StdMapWrapper tmp(other);
|
||||
std::swap(m_map, tmp.m_map);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StdMapWrapper &operator=(const StdMapWrapper &&other) {
|
||||
StdMapWrapper tmp(other);
|
||||
std::swap(tmp.m_map, m_map);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///////// Iterators
|
||||
Iterator begin() {
|
||||
return m_map.begin();
|
||||
}
|
||||
|
||||
ConstIterator begin() const {
|
||||
return m_map.begin();
|
||||
}
|
||||
|
||||
ConstIterator cbegin() const {
|
||||
return m_map.begin();
|
||||
}
|
||||
|
||||
ReverseIterator rbegin() {
|
||||
return m_map.rbegin();
|
||||
}
|
||||
|
||||
/*
|
||||
ConstReverseIterator rbegin() const {
|
||||
return m_map.rbegin();
|
||||
}
|
||||
|
||||
ConstReverseIterator crbegin() const {
|
||||
return m_map.crbegin();
|
||||
}
|
||||
*/
|
||||
|
||||
Iterator end() {
|
||||
return m_map.end();
|
||||
}
|
||||
|
||||
ConstIterator end() const {
|
||||
return m_map.end();
|
||||
}
|
||||
|
||||
ConstIterator cend() const {
|
||||
return m_map.cend();
|
||||
}
|
||||
|
||||
ReverseIterator rend() {
|
||||
return m_map.rend();
|
||||
}
|
||||
|
||||
/*
|
||||
ConstReverseIterator rend() const {
|
||||
return m_map.rend();
|
||||
}
|
||||
|
||||
ConstReverseIterator crend() const {
|
||||
return m_map.crend();
|
||||
}
|
||||
*/
|
||||
|
||||
///////// Capacity
|
||||
size_t size() const {
|
||||
return m_map.size();
|
||||
}
|
||||
|
||||
size_t max_size() const {
|
||||
return m_map.max_size();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return m_map.empty();
|
||||
}
|
||||
|
||||
|
||||
///////// Modifiers
|
||||
Iterator insert(const value_type &value) {
|
||||
return m_map.insert(value).first;
|
||||
}
|
||||
|
||||
Iterator insert(value_type &&value) {
|
||||
return m_map.insert(std::move(value)).first;
|
||||
}
|
||||
|
||||
void erase(const K &k) {
|
||||
m_map.erase(k);
|
||||
}
|
||||
|
||||
|
||||
void erase(Iterator it) {
|
||||
m_map.erase(it);
|
||||
}
|
||||
|
||||
///////// Lookup
|
||||
V &at(const K &k) {
|
||||
return m_map.at(k);
|
||||
}
|
||||
|
||||
const V &at(const K &k) const {
|
||||
return m_map.at(k);
|
||||
}
|
||||
|
||||
Iterator find(const K &k) {
|
||||
return m_map.find(k);
|
||||
}
|
||||
|
||||
ConstIterator find(const K &k) const {
|
||||
return m_map.find(k);
|
||||
}
|
||||
|
||||
V &operator[](const K &k) {
|
||||
return m_map[k];
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
base_map m_map;
|
||||
|
||||
template<typename A, typename B>
|
||||
friend
|
||||
bool operator==(const StdMapWrapper<A,B>&, const StdMapWrapper<A,B>&);
|
||||
|
||||
template<typename A, typename B>
|
||||
friend bool operator!=(const StdMapWrapper<A,B>&, const StdMapWrapper<A,B>&);
|
||||
template<typename A, typename B>
|
||||
friend bool operator<=(const StdMapWrapper<A,B>&, const StdMapWrapper<A,B>&);
|
||||
template<typename A, typename B>
|
||||
friend bool operator<(const StdMapWrapper<A,B>&, const StdMapWrapper<A,B>&);
|
||||
template<typename A, typename B>
|
||||
friend bool operator>=(const StdMapWrapper<A,B>&, const StdMapWrapper<A,B>&);
|
||||
template<typename A, typename B>
|
||||
friend bool operator>(const StdMapWrapper<A,B>&, const StdMapWrapper<A,B>&);
|
||||
};
|
||||
|
||||
template<typename K, typename T>
|
||||
bool operator==(const StdMapWrapper<K,T> &a, const StdMapWrapper<K,T> &b) {
|
||||
return a.m_map == b.m_map;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
bool operator!=(const StdMapWrapper<K,T> &a, const StdMapWrapper<K,T> &b) {
|
||||
return a.m_map != b.m_map;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
bool operator<=(const StdMapWrapper<K,T> &a, const StdMapWrapper<K,T> &b) {
|
||||
return a.m_map <= b.m_map;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
bool operator<(const StdMapWrapper<K,T> &a, const StdMapWrapper<K,T> &b) {
|
||||
return a.m_map < b.m_map;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
bool operator>=(const StdMapWrapper<K,T> &a, const StdMapWrapper<K,T> &b) {
|
||||
return a.m_map >= b.m_map;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
bool operator>(const StdMapWrapper<K,T> &a, const StdMapWrapper<K,T> &b) {
|
||||
return a.m_map > b.m_map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using Milli = std::chrono::duration<double, std::ratio<1,1000>>;
|
||||
using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
void dispTestName(const char *testName, const char *typeName) {
|
||||
std::cout << std::endl << std::endl << "************************************" << std::endl;
|
||||
std::cout << "\t" << testName << " for " << typeName << "\t" << std::endl;
|
||||
std::cout << "************************************" << std::endl << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ascendingInsert(int count, bool print = true) {
|
||||
using namespace std::chrono;
|
||||
TimePoint start, end;
|
||||
start = system_clock::now();
|
||||
T map;
|
||||
for(int i = 0; i < count; i++) {
|
||||
map.insert(std::pair<int, int>(i,i));
|
||||
}
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed = end - start;
|
||||
|
||||
if(print)
|
||||
std::cout << "Inserting " << count << " elements in aescending order took " << elapsed.count() << " milliseconds" << std::endl;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T descendingInsert(int count, bool print = true) {
|
||||
using namespace std::chrono;
|
||||
TimePoint start, end;
|
||||
start = system_clock::now();
|
||||
T map;
|
||||
for(int i = count; i > 0; i--) {
|
||||
map.insert(std::pair<int, int>(i,i));
|
||||
}
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed = end - start;
|
||||
|
||||
if(print)
|
||||
std::cout << "Inserting " << count << " elements in descending order took " << elapsed.count() << " milliseconds" << std::endl;
|
||||
return map;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void deleteTest() {
|
||||
using namespace std::chrono;
|
||||
TimePoint start, end;
|
||||
T m1 = ascendingInsert<T>(10000, false);
|
||||
T m2 = ascendingInsert<T>(100000, false);
|
||||
T m3 = ascendingInsert<T>(1000000, false);
|
||||
T m4 = ascendingInsert<T>(10000000, false);
|
||||
|
||||
std::set<int> toDelete;
|
||||
for(int i = 0; i < 10000; i++) {
|
||||
toDelete.insert(i);
|
||||
}
|
||||
|
||||
start = system_clock::now();
|
||||
for(const int e : toDelete)
|
||||
m1.erase(e);
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed1 = end - start;
|
||||
|
||||
std::cout << "deleting 10000 elements from a map of size 10000 took " << elapsed1.count() << " milliseconds" << std::endl;
|
||||
|
||||
{
|
||||
toDelete.clear();
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<int> distribution(0,99999);
|
||||
while(toDelete.size() < 10000) {
|
||||
toDelete.insert(distribution(generator));
|
||||
}
|
||||
}
|
||||
|
||||
start = system_clock::now();
|
||||
for(const int e : toDelete)
|
||||
m2.erase(e);
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed2 = end - start;
|
||||
|
||||
std::cout << "deleting 10000 elements from a map of size 100000 took " << elapsed2.count() << " milliseconds" << std::endl;
|
||||
|
||||
{
|
||||
toDelete.clear();
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<int> distribution(0,999999);
|
||||
while(toDelete.size() < 10000) {
|
||||
toDelete.insert(distribution(generator));
|
||||
}
|
||||
}
|
||||
|
||||
start = system_clock::now();
|
||||
for(const int e : toDelete)
|
||||
m3.erase(e);
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed3 = end - start;
|
||||
|
||||
std::cout << "deleting 10000 elements from a map of size 1000000 took " << elapsed3.count() << " milliseconds" << std::endl;
|
||||
|
||||
{
|
||||
toDelete.clear();
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<int> distribution(0,9999999);
|
||||
while(toDelete.size() < 10000) {
|
||||
toDelete.insert(distribution(generator));
|
||||
}
|
||||
}
|
||||
|
||||
start = system_clock::now();
|
||||
for(const int e : toDelete)
|
||||
m4.erase(e);
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed4 = end - start;
|
||||
|
||||
std::cout << "deleting 10000 elements from a map of size 10000000 took " << elapsed4.count() << " milliseconds" << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void findTest() {
|
||||
using namespace std::chrono;
|
||||
TimePoint start, end;
|
||||
T m1 = ascendingInsert<T>(10000, false);
|
||||
T m2 = ascendingInsert<T>(100000, false);
|
||||
T m3 = ascendingInsert<T>(1000000, false);
|
||||
T m4 = ascendingInsert<T>(10000000, false);
|
||||
T m11;
|
||||
T m22;
|
||||
T m33;
|
||||
T m44;
|
||||
|
||||
std::vector<int> toFind;
|
||||
for(int i = 0; i < 10000; i++) {
|
||||
toFind.push_back(i);
|
||||
}
|
||||
|
||||
start = system_clock::now();
|
||||
for(const int e : toFind) {
|
||||
auto it = m1.find(e);
|
||||
m11.insert(*it);
|
||||
}
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed1 = end - start;
|
||||
|
||||
std::cout << "Finding 10000 elements from a map of size " << m1.size() << " took " << elapsed1.count() << " milliseconds" << std::endl;
|
||||
|
||||
{
|
||||
toFind.clear();
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<int> distribution(0,99999);
|
||||
while(toFind.size() < 10000) {
|
||||
toFind.push_back(distribution(generator));
|
||||
}
|
||||
}
|
||||
|
||||
start = system_clock::now();
|
||||
for(const int e : toFind) {
|
||||
auto it = m2.find(e);
|
||||
m22.insert(*it);
|
||||
}
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed2 = end - start;
|
||||
|
||||
std::cout << "Finding 10000 elements from a map of size " << m2.size() << " took " << elapsed2.count() << " milliseconds" << std::endl;
|
||||
|
||||
{
|
||||
toFind.clear();
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<int> distribution(0,999999);
|
||||
while(toFind.size() < 10000) {
|
||||
toFind.push_back(distribution(generator));
|
||||
}
|
||||
}
|
||||
|
||||
start = system_clock::now();
|
||||
for(const int e : toFind) {
|
||||
auto it = m3.find(e);
|
||||
m33.insert(*it);
|
||||
}
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed3 = end - start;
|
||||
|
||||
std::cout << "Finding 10000 elements from a map of size " << m3.size() << " took " << elapsed3.count() << " milliseconds" << std::endl;
|
||||
|
||||
{
|
||||
toFind.clear();
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<int> distribution(0,9999999);
|
||||
while(toFind.size() < 10000) {
|
||||
toFind.push_back(distribution(generator));
|
||||
}
|
||||
}
|
||||
|
||||
start = system_clock::now();
|
||||
for(const int e : toFind) {
|
||||
auto it = m4.find(e);
|
||||
m44.insert(*it);
|
||||
}
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed4 = end - start;
|
||||
|
||||
std::cout << "Finding 10000 elements from a map of size " << m4.size() << " took " << elapsed4.count() << " milliseconds" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void iterationTest(int count) {
|
||||
using namespace std::chrono;
|
||||
T m = ascendingInsert<T>(count,false);
|
||||
|
||||
TimePoint start, end;
|
||||
|
||||
for(int j = 0; j < 3; j++) {
|
||||
start = system_clock::now();
|
||||
for(auto it = m.begin(); it != m.end(); it++) {
|
||||
if(j==2)
|
||||
(*it).second += j;
|
||||
}
|
||||
end = system_clock::now();
|
||||
}
|
||||
|
||||
Milli elapsed = end - start;
|
||||
|
||||
std::cout << "Iterating across " << count << " elements in a map of size " << count << " took " << elapsed.count() << " milliseconds time per iteration was " << elapsed.count()/double(count)*1e6 << " nanoseconds" << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void copyTest(int count) {
|
||||
using namespace std::chrono;
|
||||
T m = ascendingInsert<T>(count,false);
|
||||
|
||||
TimePoint start, end;
|
||||
|
||||
start = system_clock::now();
|
||||
T m2(m);
|
||||
end = system_clock::now();
|
||||
|
||||
Milli elapsed = end - start;
|
||||
|
||||
std::cout << "Copy construction of a map of size " << m2.size() << " took " << elapsed.count() << " milliseconds" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
#include <assert.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ostream &
|
||||
operator<<(ostream &os, const type_info &ti) {
|
||||
int ec;
|
||||
const char *demangled_name = abi::__cxa_demangle(ti.name(), 0, 0, &ec);
|
||||
assert(ec == 0);
|
||||
os << demangled_name;
|
||||
free((void *) demangled_name);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void foo(T &&o) {
|
||||
//o = 2;
|
||||
cout << typeid(const int &) << endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
const int i = 1;
|
||||
foo(i);
|
||||
}
|
||||
*/
|
||||
|
||||
class comma_numpunct : public std::numpunct<char> {
|
||||
protected:
|
||||
virtual char do_thousands_sep() const { return ','; }
|
||||
virtual std::string do_grouping() const { return "\03"; }
|
||||
};
|
||||
|
||||
|
||||
int main() {
|
||||
//separate all printed numbers with commas
|
||||
std::locale comma_locale(std::locale(), new comma_numpunct());
|
||||
std::cout.imbue(comma_locale);
|
||||
|
||||
auto demangle = [](const std::type_info &ti) {
|
||||
int ec;
|
||||
return abi::__cxa_demangle(ti.name(), 0, 0, &ec);
|
||||
assert(ec == 0);
|
||||
};
|
||||
|
||||
const char *w = demangle(typeid(cs440::StdMapWrapper<int,int>));
|
||||
const char *m = demangle(typeid(cs440::Map<int,int>));
|
||||
|
||||
{
|
||||
dispTestName("Ascending insert", m);
|
||||
ascendingInsert<cs440::Map<int,int>>(1000);
|
||||
ascendingInsert<cs440::Map<int,int>>(10000);
|
||||
ascendingInsert<cs440::Map<int,int>>(100000);
|
||||
ascendingInsert<cs440::Map<int,int>>(1000000);
|
||||
ascendingInsert<cs440::Map<int,int>>(10000000);
|
||||
dispTestName("Ascending insert", w);
|
||||
ascendingInsert<cs440::StdMapWrapper<int,int>>(1000);
|
||||
ascendingInsert<cs440::StdMapWrapper<int,int>>(10000);
|
||||
ascendingInsert<cs440::StdMapWrapper<int,int>>(100000);
|
||||
ascendingInsert<cs440::StdMapWrapper<int,int>>(1000000);
|
||||
ascendingInsert<cs440::StdMapWrapper<int,int>>(10000000);
|
||||
}
|
||||
|
||||
{
|
||||
dispTestName("Descending insert", m);
|
||||
descendingInsert<cs440::Map<int,int>>(1000);
|
||||
descendingInsert<cs440::Map<int,int>>(10000);
|
||||
descendingInsert<cs440::Map<int,int>>(100000);
|
||||
descendingInsert<cs440::Map<int,int>>(1000000);
|
||||
descendingInsert<cs440::Map<int,int>>(10000000);
|
||||
dispTestName("Descending insert", w);
|
||||
descendingInsert<cs440::StdMapWrapper<int,int>>(1000);
|
||||
descendingInsert<cs440::StdMapWrapper<int,int>>(10000);
|
||||
descendingInsert<cs440::StdMapWrapper<int,int>>(100000);
|
||||
descendingInsert<cs440::StdMapWrapper<int,int>>(1000000);
|
||||
descendingInsert<cs440::StdMapWrapper<int,int>>(10000000);
|
||||
}
|
||||
|
||||
{
|
||||
dispTestName("Delete test", m);
|
||||
deleteTest<cs440::Map<int,int>>();
|
||||
dispTestName("Delete test", w);
|
||||
deleteTest<cs440::StdMapWrapper<int,int>>();
|
||||
}
|
||||
|
||||
{
|
||||
dispTestName("Find test", m);
|
||||
findTest<cs440::Map<int,int>>();
|
||||
dispTestName("Find test", w);
|
||||
findTest<cs440::StdMapWrapper<int,int>>();
|
||||
}
|
||||
|
||||
/*
|
||||
Remember that some of these maps get quite large - iteration times may be affected by things other than the scaling of your algorithm.
|
||||
How do the many levels of the memory heirarchy in a computer relate?
|
||||
How do they perform relative to one another?
|
||||
How might this have affected other performance tests?
|
||||
*/
|
||||
{
|
||||
dispTestName("Iteration test", m);
|
||||
iterationTest<cs440::Map<int,int>>(10000);
|
||||
iterationTest<cs440::Map<int,int>>(20000);
|
||||
iterationTest<cs440::Map<int,int>>(40000);
|
||||
iterationTest<cs440::Map<int,int>>(80000);
|
||||
iterationTest<cs440::Map<int,int>>(160000);
|
||||
iterationTest<cs440::Map<int,int>>(320000);
|
||||
iterationTest<cs440::Map<int,int>>(640000);
|
||||
iterationTest<cs440::Map<int,int>>(1280000);
|
||||
iterationTest<cs440::Map<int,int>>(2560000);
|
||||
iterationTest<cs440::Map<int,int>>(5120000);
|
||||
#if DO_BIG_ITERATION_TEST
|
||||
//Optional test. This is more ram than the remote machines have and will likely take a long time to run.
|
||||
iterationTest<cs440::Map<int,int>>(600000000);
|
||||
#endif
|
||||
dispTestName("Iteration test", w);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(10000);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(20000);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(40000);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(80000);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(160000);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(320000);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(640000);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(1280000);
|
||||
iterationTest<cs440::StdMapWrapper<int,int>>(5120000);
|
||||
#if DO_BIG_ITERATION_TEST
|
||||
//Optional test. This is more ram than the remote machines have and will likely take a long time to run.
|
||||
iterationTest<cs440::Map<int,int>>(600000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
//Test copy constructor scaling
|
||||
dispTestName("Copy test", m);
|
||||
copyTest<cs440::Map<int,int>>(10000);
|
||||
copyTest<cs440::Map<int,int>>(100000);
|
||||
copyTest<cs440::Map<int,int>>(1000000);
|
||||
copyTest<cs440::Map<int,int>>(10000000);
|
||||
dispTestName("Copy test", w);
|
||||
copyTest<cs440::StdMapWrapper<int,int>>(10000);
|
||||
copyTest<cs440::StdMapWrapper<int,int>>(100000);
|
||||
copyTest<cs440::StdMapWrapper<int,int>>(1000000);
|
||||
copyTest<cs440::StdMapWrapper<int,int>>(10000000);
|
||||
}
|
||||
|
||||
//Add your own indexibility scaling test here
|
||||
|
||||
// Cast, due to const-ness.
|
||||
free((void *) w);
|
||||
free((void *) m);
|
||||
}
|
140
test.cpp
140
test.cpp
|
@ -1,140 +0,0 @@
|
|||
#include "Map.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
|
||||
void stress(int stress_size) {
|
||||
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
std::default_random_engine gen(seed);
|
||||
std::uniform_int_distribution<unsigned int> dist(0, 10000);
|
||||
|
||||
cs440::Map<int, int> m;
|
||||
|
||||
for(int i = 0; i < stress_size; ++i) {
|
||||
auto g = dist(gen);
|
||||
m.insert({g, g});
|
||||
}
|
||||
|
||||
int num_erases = gen() % m.size();
|
||||
for(int i = 0; i < num_erases; ++i) {
|
||||
//select a random element
|
||||
int choice = gen() % m.size();
|
||||
auto iter = std::begin(m);
|
||||
for (int j = 0; j < choice; ++j) {
|
||||
++iter;
|
||||
}
|
||||
|
||||
m.erase(iter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// unrealistic examples
|
||||
void access_by_key() {
|
||||
cs440::Map<int, long> m;
|
||||
m.insert({10, 10});
|
||||
m.insert({3, 3});
|
||||
m.insert({20, 20});
|
||||
m.insert({15, 15});
|
||||
|
||||
m.at(10);
|
||||
bool thrown = false;
|
||||
try {
|
||||
m.at(10000);
|
||||
} catch (std::out_of_range) {
|
||||
thrown = true;
|
||||
}
|
||||
assert(thrown); // the .at should have thrown an exception
|
||||
|
||||
const auto& m_ref = m;
|
||||
m_ref.at(10); // const .at
|
||||
|
||||
auto iter = m.find(3);
|
||||
assert((*iter).second == 3);
|
||||
|
||||
auto iter2 = m.find(100000); // not in map, should give iterator to end()
|
||||
assert(iter2 == std::end(m));
|
||||
|
||||
m[30] = 30; // should default construct
|
||||
|
||||
m.erase(10);
|
||||
assert(m.find(10) == std::end(m)); // 10 shouldn't be in the map anymore
|
||||
}
|
||||
|
||||
void count_words() {
|
||||
cs440::Map<std::string, int> words_count;
|
||||
|
||||
// just a big list of words
|
||||
auto words = {"this", "is", "a", "string", "with", "words", "some",
|
||||
"words", "in", "the", "string", "repeat", "the", "more", "they",
|
||||
"repeat", "the", "more", "they", "should", "count", "they", "more",
|
||||
"they", "count", "the", "more", "they", "will", "have", "their",
|
||||
"count", "increased"};
|
||||
|
||||
for (auto& word : words) {
|
||||
// this works because int can be default constructed, and the
|
||||
// default of int (by doing int{} ) is 0.
|
||||
words_count[word] += 1; // add 1 to the count
|
||||
}
|
||||
|
||||
// print the frequency of each word
|
||||
std::cout << "word frequency:\n";
|
||||
for (auto& count : words_count) { // uses .begin() and .end()
|
||||
std::cout << count.first << ": " << count.second << '\n';
|
||||
}
|
||||
|
||||
std::cout << "word frequency reversed order:\n";
|
||||
for (auto riter = words_count.rbegin();
|
||||
riter != words_count.rend();
|
||||
++riter) {
|
||||
std::cout << (*riter).first << ": " << (*riter).second << '\n';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// creates a mapping from the values in the range [low, high) to their cubes
|
||||
cs440::Map<int, int> cubes(int low, int high) {
|
||||
cs440::Map<int, int> cb;
|
||||
for (int i = low; i < high; ++i) {
|
||||
cb.insert({i, i*i*i});
|
||||
}
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
|
||||
int main () {
|
||||
count_words();
|
||||
|
||||
auto cube = cubes(-5, 10); // move construct returned value
|
||||
std::cout << cube.at(-2) << '\n'; // -8
|
||||
std::cout << cube.at(5) << '\n'; // 125
|
||||
const int n = 30;
|
||||
try {
|
||||
std::cout << cube.at(n) << '\n'; // 30 is not in the Map
|
||||
} catch (std::out_of_range) {
|
||||
std::cout << n << " not in cubes range\n";
|
||||
}
|
||||
|
||||
// constructors and assignment examples:
|
||||
// initializer_list example
|
||||
cs440::Map<int, double> int_double_map {{1, 1.0}, {3, 5.67}, {13, 6.9}};
|
||||
|
||||
// must support copy construction
|
||||
cs440::Map<int, double> copy_example{int_double_map};
|
||||
|
||||
cs440::Map<int, double> assign_example;
|
||||
// must support copy assignment
|
||||
assign_example = copy_example;
|
||||
|
||||
access_by_key();
|
||||
stress(10000);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue