slowly working through given tests to get Map fully working in all functionality
This commit is contained in:
parent
315a200c9b
commit
ce0f5f7ef0
2 changed files with 125 additions and 137 deletions
105
Map.hpp
105
Map.hpp
|
@ -4,7 +4,6 @@
|
||||||
// uncomment on submission/performance test
|
// uncomment on submission/performance test
|
||||||
// #define NDEBUG
|
// #define NDEBUG
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
@ -32,8 +31,8 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
|
|
||||||
struct Node {
|
struct Node {
|
||||||
int valid = 0x13371337;
|
int valid = 0x13371337;
|
||||||
Node *parent;
|
Node *parent = nullptr;
|
||||||
internal_ValueType val;
|
std::unique_ptr<internal_ValueType> val;
|
||||||
std::unique_ptr<Node> left;
|
std::unique_ptr<Node> left;
|
||||||
std::unique_ptr<Node> right;
|
std::unique_ptr<Node> right;
|
||||||
Color color;
|
Color color;
|
||||||
|
@ -41,13 +40,14 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
Node *next;
|
Node *next;
|
||||||
Map *map;
|
Map *map;
|
||||||
Node(internal_ValueType val, Map *map)
|
Node(internal_ValueType val, Map *map)
|
||||||
: parent{nullptr}, val{val}, left{}, right{}, color{Color::Red},
|
: parent{nullptr}, val{new internal_ValueType{val}}, left{}, right{},
|
||||||
prev{nullptr}, next{nullptr}, map{map} {}
|
color{Color::Red}, prev{nullptr}, next{nullptr}, map{map} {}
|
||||||
Node(const Node &rhs)
|
Node(const Node &rhs)
|
||||||
: parent{nullptr}, val{rhs.val},
|
: parent{nullptr},
|
||||||
left{std::make_unique<Node>(*rhs.left)},
|
val{rhs.val ? new internal_ValueType{*rhs.val} : nullptr},
|
||||||
right{std::make_unique<Node>(*rhs.right)}, color{rhs.color},
|
left{rhs.left ? std::make_unique<Node>(*rhs.left) : nullptr},
|
||||||
prev{nullptr}, next{nullptr}, map{rhs.map} {
|
right{rhs.right ? std::make_unique<Node>(*rhs.right) : nullptr},
|
||||||
|
color{rhs.color}, prev{nullptr}, next{nullptr}, map{rhs.map} {
|
||||||
this->valid = 0x13371337;
|
this->valid = 0x13371337;
|
||||||
|
|
||||||
if (this->left) {
|
if (this->left) {
|
||||||
|
@ -64,11 +64,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
: parent{nullptr}, val{std::move(rhs.val)}, left{std::move(rhs.left)},
|
: parent{nullptr}, val{std::move(rhs.val)}, left{std::move(rhs.left)},
|
||||||
right{std::move(rhs.right)}, color{rhs.color}, prev{nullptr},
|
right{std::move(rhs.right)}, color{rhs.color}, prev{nullptr},
|
||||||
next{nullptr}, map{rhs.map} {
|
next{nullptr}, map{rhs.map} {
|
||||||
// TODO: remove on finish
|
|
||||||
|
|
||||||
if (rhs.valid != 0x13371337) {
|
|
||||||
std::cerr << "(" << rhs.val.first << ")" << std::endl;
|
|
||||||
}
|
|
||||||
rhs.valid = 0;
|
rhs.valid = 0;
|
||||||
this->valid = 0x13371337;
|
this->valid = 0x13371337;
|
||||||
|
|
||||||
|
@ -88,9 +84,12 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
// to a rotation where parent can get wonky
|
// to a rotation where parent can get wonky
|
||||||
// this->parent
|
// this->parent
|
||||||
|
|
||||||
this->val = rhs.val;
|
this->val =
|
||||||
this->left = std::make_unique<Node>(*rhs.left);
|
rhs.val ? std::unique_ptr<internal_ValueType>{new internal_ValueType{
|
||||||
this->right = std::make_unique<Node>(*rhs.right);
|
*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->color = rhs.color;
|
this->color = rhs.color;
|
||||||
this->valid = 0x13371337;
|
this->valid = 0x13371337;
|
||||||
|
|
||||||
|
@ -110,7 +109,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
// retain parent as is, common case is the copy or move is happening due
|
// retain parent as is, common case is the copy or move is happening due
|
||||||
// to a rotation where parent can get wonky
|
// to a rotation where parent can get wonky
|
||||||
// this->parent
|
// this->parent
|
||||||
this->val = rhs.val;
|
this->val = std::move(rhs.val);
|
||||||
this->left = std::move(rhs.left);
|
this->left = std::move(rhs.left);
|
||||||
this->right = std::move(rhs.right);
|
this->right = std::move(rhs.right);
|
||||||
this->color = rhs.color;
|
this->color = rhs.color;
|
||||||
|
@ -532,8 +531,26 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
Map() : root{}, _size{0} {}
|
Map() : root{}, _size{0} {}
|
||||||
Map(const Map &rhs) : root{rhs.root}, _size{rhs._size} {}
|
Map(const Map &rhs) : root{rhs.root}, _size{rhs._size} {
|
||||||
Map(Map &&rhs) : root{std::move(rhs.root)}, _size{rhs._size} {}
|
this->min = &this->root.value();
|
||||||
|
this->max = &this->root.value();
|
||||||
|
while (min->left) {
|
||||||
|
min = min->left.get();
|
||||||
|
}
|
||||||
|
while (min->right) {
|
||||||
|
min = min->left.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map(Map &&rhs) : root{std::move(rhs.root)}, _size{rhs._size} {
|
||||||
|
this->min = &this->root.value();
|
||||||
|
this->max = &this->root.value();
|
||||||
|
while (min->left) {
|
||||||
|
min = min->left.get();
|
||||||
|
}
|
||||||
|
while (min->right) {
|
||||||
|
min = min->left.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
Map &operator=(const Map &rhs) {
|
Map &operator=(const Map &rhs) {
|
||||||
this->root = rhs.root;
|
this->root = rhs.root;
|
||||||
this->_size = rhs._size;
|
this->_size = rhs._size;
|
||||||
|
@ -550,8 +567,8 @@ public:
|
||||||
void check() {
|
void check() {
|
||||||
assert(!this->root || this->root.value().color == Color::Black);
|
assert(!this->root || this->root.value().color == Color::Black);
|
||||||
}
|
}
|
||||||
std::size_t size() { return this->_size; }
|
std::size_t size() const { return this->_size; }
|
||||||
bool empty() { return this->size() == 0; }
|
bool empty() const { return this->size() == 0; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// private helpers
|
// private helpers
|
||||||
|
@ -604,53 +621,28 @@ private:
|
||||||
Direction ret_dir;
|
Direction ret_dir;
|
||||||
// map is empty
|
// map is empty
|
||||||
if (!this->root.has_value()) {
|
if (!this->root.has_value()) {
|
||||||
if constexpr (trace) {
|
|
||||||
std::cerr << "(map empty)" << std::endl;
|
|
||||||
}
|
|
||||||
return std::make_pair(nullptr, ret_dir);
|
return std::make_pair(nullptr, ret_dir);
|
||||||
}
|
}
|
||||||
if constexpr (trace) {
|
|
||||||
std::cerr << "root";
|
|
||||||
}
|
|
||||||
// value is in root
|
// value is in root
|
||||||
if (this->root.value().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);
|
return std::make_pair(nullptr, ret_dir);
|
||||||
}
|
}
|
||||||
ret_parent = &this->root.value();
|
ret_parent = &this->root.value();
|
||||||
if (key < ret_parent->val.first) {
|
if (key < ret_parent->val->first) {
|
||||||
if constexpr (trace) {
|
|
||||||
std::cerr << "->left";
|
|
||||||
}
|
|
||||||
ret_dir = Direction::Left;
|
ret_dir = Direction::Left;
|
||||||
} else {
|
} else {
|
||||||
if constexpr (trace) {
|
|
||||||
std::cerr << "->right";
|
|
||||||
}
|
|
||||||
ret_dir = Direction::Right;
|
ret_dir = Direction::Right;
|
||||||
}
|
}
|
||||||
while (ret_parent->child(ret_dir) &&
|
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);
|
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;
|
ret_dir = Direction::Left;
|
||||||
} else {
|
} else {
|
||||||
if constexpr (trace) {
|
|
||||||
std::cerr << "->right";
|
|
||||||
}
|
|
||||||
ret_dir = Direction::Right;
|
ret_dir = Direction::Right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constexpr (trace) {
|
|
||||||
std::cerr << "->found" << std::endl;
|
|
||||||
}
|
|
||||||
return std::make_pair(ret_parent, ret_dir);
|
return std::make_pair(ret_parent, ret_dir);
|
||||||
}
|
}
|
||||||
void hard_erase(Node *n) {
|
void hard_erase(Node *n) {
|
||||||
|
@ -754,7 +746,7 @@ private:
|
||||||
// 2 children
|
// 2 children
|
||||||
if (erasing->left && erasing->right) {
|
if (erasing->left && erasing->right) {
|
||||||
Node *succ = erasing->next;
|
Node *succ = erasing->next;
|
||||||
erasing->val = succ->val;
|
erasing->val = std::move(succ->val);
|
||||||
this->core_erase(succ);
|
this->core_erase(succ);
|
||||||
}
|
}
|
||||||
// 1 child
|
// 1 child
|
||||||
|
@ -800,7 +792,7 @@ public:
|
||||||
auto [parent, dir] = locate(key);
|
auto [parent, dir] = locate(key);
|
||||||
if (parent == nullptr) {
|
if (parent == nullptr) {
|
||||||
if (this->root.has_value()) {
|
if (this->root.has_value()) {
|
||||||
if (this->root.value().val.first == key) {
|
if (this->root.value().val->first == key) {
|
||||||
return Iterator{&this->root.value()};
|
return Iterator{&this->root.value()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -815,7 +807,7 @@ public:
|
||||||
auto [parent, dir] = locate(key);
|
auto [parent, dir] = locate(key);
|
||||||
if (parent == nullptr) {
|
if (parent == nullptr) {
|
||||||
if (this->root.has_value()) {
|
if (this->root.has_value()) {
|
||||||
if (this->root.value().val.first == key) {
|
if (this->root.value().val->first == key) {
|
||||||
return Iterator{const_cast<Node *>(&this->root.value())};
|
return Iterator{const_cast<Node *>(&this->root.value())};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -881,7 +873,7 @@ public:
|
||||||
if (core_erase(pos.underlying)) {
|
if (core_erase(pos.underlying)) {
|
||||||
pos.underlying->restore_ordering();
|
pos.underlying->restore_ordering();
|
||||||
} else {
|
} else {
|
||||||
if (before != nullptr) {
|
if (before != nullptr && before->valid == 0x13371337) {
|
||||||
before->next = before->calc_succ();
|
before->next = before->calc_succ();
|
||||||
if (before->next != nullptr) {
|
if (before->next != nullptr) {
|
||||||
before->next->prev = before;
|
before->next->prev = before;
|
||||||
|
@ -889,7 +881,7 @@ public:
|
||||||
} else {
|
} else {
|
||||||
this->min = after;
|
this->min = after;
|
||||||
}
|
}
|
||||||
if (after != nullptr) {
|
if (after != nullptr && after->valid == 0x13371337) {
|
||||||
after->prev = after->calc_pred();
|
after->prev = after->calc_pred();
|
||||||
if (after->prev != nullptr) {
|
if (after->prev != nullptr) {
|
||||||
after->prev->next = after;
|
after->prev->next = after;
|
||||||
|
@ -925,7 +917,7 @@ public:
|
||||||
if (ret == this->end()) {
|
if (ret == this->end()) {
|
||||||
throw std::out_of_range{"key not in map"};
|
throw std::out_of_range{"key not in map"};
|
||||||
}
|
}
|
||||||
return (*ret).first;
|
return (*ret).second;
|
||||||
}
|
}
|
||||||
Mapped_T &operator[](const Key_T &key) {
|
Mapped_T &operator[](const Key_T &key) {
|
||||||
this->insert({key, {}});
|
this->insert({key, {}});
|
||||||
|
@ -939,7 +931,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void erase(const Key_T &key) { this->erase(this->find(key)); }
|
void erase(const Key_T &key) { this->erase(this->find(key)); }
|
||||||
// TODO:
|
|
||||||
friend bool operator==(const Map &lhs, const Map &rhs) {
|
friend bool operator==(const Map &lhs, const Map &rhs) {
|
||||||
if (lhs.size() != rhs.size()) {
|
if (lhs.size() != rhs.size()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
155
minimal.cpp
155
minimal.cpp
|
@ -4,119 +4,116 @@
|
||||||
|
|
||||||
// basically an int wrapper
|
// basically an int wrapper
|
||||||
class MyKeyType {
|
class MyKeyType {
|
||||||
private:
|
private:
|
||||||
int val;
|
int val;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//not default constructable, not copy assignable, not move assignable
|
// not default constructable, not copy assignable, not move assignable
|
||||||
MyKeyType() = delete;
|
MyKeyType() = delete;
|
||||||
MyKeyType& operator=(const MyKeyType&) = delete;
|
MyKeyType &operator=(const MyKeyType &) = delete;
|
||||||
MyKeyType& operator=(MyKeyType&&) = delete;
|
MyKeyType &operator=(MyKeyType &&) = delete;
|
||||||
|
|
||||||
// copy constructable and move assignable
|
// copy constructable and move assignable
|
||||||
MyKeyType(MyKeyType&&) = default;
|
MyKeyType(MyKeyType &&) = default;
|
||||||
MyKeyType(const MyKeyType&) = default;
|
MyKeyType(const MyKeyType &) = default;
|
||||||
~MyKeyType() = default;
|
~MyKeyType() = default;
|
||||||
|
|
||||||
MyKeyType(int i) : val(i) { }
|
MyKeyType(int i) : val(i) {}
|
||||||
|
|
||||||
bool operator<(const MyKeyType& other) const {
|
bool operator<(const MyKeyType &other) const { return this->val < other.val; }
|
||||||
return this->val < other.val;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const MyKeyType &other) const {
|
bool operator==(const MyKeyType &other) const {
|
||||||
return this->val == other.val;
|
return this->val == other.val;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// same as keytype except no operator<
|
// same as keytype except no operator<
|
||||||
class MyValueType {
|
class MyValueType {
|
||||||
private:
|
private:
|
||||||
int val;
|
int val;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//not default constructable, not copy assignable, not move assignable
|
// not default constructable, not copy assignable, not move assignable
|
||||||
MyValueType() = delete;
|
MyValueType() = delete;
|
||||||
MyValueType& operator=(const MyValueType&) = delete;
|
MyValueType &operator=(const MyValueType &) = delete;
|
||||||
MyValueType& operator=(MyValueType&&) = delete;
|
MyValueType &operator=(MyValueType &&) = delete;
|
||||||
|
|
||||||
// copy constructable and move assignable
|
// copy constructable and move assignable
|
||||||
MyValueType(MyValueType&&) = default;
|
MyValueType(MyValueType &&) = default;
|
||||||
MyValueType(const MyValueType&) = default;
|
MyValueType(const MyValueType &) = default;
|
||||||
~MyValueType() = default;
|
~MyValueType() = default;
|
||||||
|
|
||||||
MyValueType(int i) : val(i) { }
|
MyValueType(int i) : val(i) {}
|
||||||
|
|
||||||
bool operator==(const MyValueType &other) const {
|
bool operator==(const MyValueType &other) const {
|
||||||
return this->val == other.val;
|
return this->val == other.val;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MyDefaultConstructible {
|
class MyDefaultConstructible {
|
||||||
|
|
||||||
friend bool operator<(const MyDefaultConstructible &o1, const MyDefaultConstructible &o2) {
|
friend bool operator<(const MyDefaultConstructible &o1,
|
||||||
return o1.val < o2.val;
|
const MyDefaultConstructible &o2) {
|
||||||
}
|
return o1.val < o2.val;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int val = 0;
|
int val = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// not copy assignable, not move assignable
|
// not copy assignable, not move assignable
|
||||||
MyDefaultConstructible& operator=(const MyDefaultConstructible&) = delete;
|
MyDefaultConstructible &operator=(const MyDefaultConstructible &) = delete;
|
||||||
MyDefaultConstructible& operator=(MyDefaultConstructible&&) = delete;
|
MyDefaultConstructible &operator=(MyDefaultConstructible &&) = delete;
|
||||||
|
|
||||||
// default constructable, copy constructable and move assignable
|
// default constructable, copy constructable and move assignable
|
||||||
MyDefaultConstructible() = default;
|
MyDefaultConstructible() = default;
|
||||||
MyDefaultConstructible(MyDefaultConstructible&&) = default;
|
MyDefaultConstructible(MyDefaultConstructible &&) = default;
|
||||||
MyDefaultConstructible(const MyDefaultConstructible&) = default;
|
MyDefaultConstructible(const MyDefaultConstructible &) = default;
|
||||||
~MyDefaultConstructible() = default;
|
~MyDefaultConstructible() = default;
|
||||||
|
|
||||||
MyDefaultConstructible(int i) : val(i) { }
|
MyDefaultConstructible(int i) : val(i) {}
|
||||||
|
|
||||||
bool operator==(const MyDefaultConstructible &other) const {
|
bool operator==(const MyDefaultConstructible &other) const {
|
||||||
return this->val == other.val;
|
return this->val == other.val;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class MyAssignable {
|
class MyAssignable {
|
||||||
private:
|
private:
|
||||||
int val = 0;
|
int val = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MyAssignable() = default;
|
MyAssignable() = default;
|
||||||
MyAssignable(int i) : val(i) { }
|
MyAssignable(int i) : val(i) {}
|
||||||
bool operator==(const MyAssignable &other) const {
|
bool operator==(const MyAssignable &other) const {
|
||||||
return this->val == other.val;
|
return this->val == other.val;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// manual instantiation, instantiates every member function instead of
|
// manual instantiation, instantiates every member function instead of
|
||||||
// just the ones called
|
// just the ones called
|
||||||
template class cs440::Map<MyKeyType, MyDefaultConstructible>;
|
template class cs440::Map<MyKeyType, MyDefaultConstructible>;
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
cs440::Map<MyKeyType, MyValueType> m{{3, 5}};
|
cs440::Map<MyKeyType, MyValueType> m{{3, 5}};
|
||||||
m.insert({{2}, {3}});
|
m.insert({{2}, {3}});
|
||||||
m.insert({{1}, {3}});
|
m.insert({{1}, {3}});
|
||||||
m.insert({{5}, {3}});
|
m.insert({{5}, {3}});
|
||||||
m.insert({{7}, {3}});
|
m.insert({{7}, {3}});
|
||||||
m.at(2);
|
m.at(2);
|
||||||
auto iter = m.find(2);
|
auto iter = m.find(2);
|
||||||
m.erase(iter);
|
m.erase(iter);
|
||||||
auto m_copy = m;
|
auto m_copy = m;
|
||||||
assert(m_copy == m);
|
assert(m_copy == m);
|
||||||
|
|
||||||
cs440::Map<MyKeyType, MyDefaultConstructible> m2{{8, 9}};
|
cs440::Map<MyKeyType, MyDefaultConstructible> m2{{8, 9}};
|
||||||
m2[10]; // should default construct these values
|
m2[10]; // should default construct these values
|
||||||
m2[15];
|
m2[15];
|
||||||
|
|
||||||
cs440::Map<MyKeyType, MyAssignable> m3{{6, 7}};
|
cs440::Map<MyKeyType, MyAssignable> m3{{6, 7}};
|
||||||
m3[20] = {5}; // move assign
|
m3[20] = {5}; // move assign
|
||||||
MyAssignable ma{1};
|
MyAssignable ma{1};
|
||||||
m3[10] = ma; //copy assign
|
m3[10] = ma; // copy assign
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue