375 lines
10 KiB
C++
375 lines
10 KiB
C++
// commenting everything out when I commit so all commits my code technically
|
|
// compiles
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <deque>
|
|
#include <initializer_list>
|
|
#include <iterator>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
// everything is super interconnected so some forward declarations are needed at
|
|
// various points
|
|
namespace cs440 {
|
|
|
|
template <typename Key_T, typename Mapped_T> class Map;
|
|
|
|
namespace {
|
|
enum class Color { Red, Black };
|
|
template <typename Key_T, typename Mapped_T> struct BookKeeping {
|
|
friend class Map<Key_T, Mapped_T>;
|
|
using ValueType = std::pair<const Key_T, Mapped_T>;
|
|
Map<Key_T, Mapped_T> &parent;
|
|
ValueType value;
|
|
std::size_t self;
|
|
Color color;
|
|
|
|
std::optional<std::size_t> next;
|
|
std::optional<std::size_t> prev;
|
|
};
|
|
} // namespace
|
|
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
|
template <typename Key_T, typename Mapped_T> class Map {
|
|
private:
|
|
using ValueType = std::pair<const Key_T, Mapped_T>;
|
|
// idx 0 = root
|
|
// left = parent * 2 + 1
|
|
// right = parent * 2 + 2
|
|
std::vector<std::optional<BookKeeping<Key_T, Mapped_T>>> store;
|
|
std::vector<Color> coloration;
|
|
|
|
public:
|
|
class Iterator;
|
|
class ConstIterator;
|
|
class ReverseIterator;
|
|
|
|
friend class Iterator;
|
|
friend class ConstIterator;
|
|
friend class ReverseIterator;
|
|
friend struct BookKeeping<Key_T, Mapped_T>;
|
|
// TODO: Iterator functionality
|
|
class Iterator {
|
|
friend class Map<Key_T, Mapped_T>;
|
|
friend struct BookKeeping<Key_T, Mapped_T>;
|
|
|
|
public:
|
|
private:
|
|
enum class PastElem { start, end, neither };
|
|
// pointer needed so we can replace as needed
|
|
BookKeeping<Key_T, Mapped_T> *parent;
|
|
|
|
// TODO: next/prev found in bookkeeping
|
|
// Note: only used when past first/last element
|
|
PastElem use;
|
|
std::optional<std::size_t> next_or_prev;
|
|
Iterator(std::optional<BookKeeping<Key_T, Mapped_T>> &parent)
|
|
: parent{&parent}, use{PastElem::neither}, next_or_prev{std::nullopt} {}
|
|
|
|
public:
|
|
Iterator() = delete;
|
|
ConstIterator to_const() const { return ConstIterator(*this); }
|
|
Iterator &operator++() { return *this; }
|
|
Iterator operator++(int) {
|
|
Iterator tmp = *this;
|
|
++(*this);
|
|
return tmp;
|
|
}
|
|
Iterator &operator--() { return *this; }
|
|
Iterator operator--(int) {
|
|
Iterator tmp = *this;
|
|
--(*this);
|
|
return tmp;
|
|
}
|
|
ValueType &operator*() const { return parent.parent.at(parent.self); }
|
|
ValueType *operator->() const { return &**this; }
|
|
friend bool operator==(Iterator const &lhs, Iterator const &rhs) {
|
|
return lhs.store_iter == rhs.store_iter;
|
|
}
|
|
friend bool operator!=(Iterator const &lhs, Iterator const &rhs) {
|
|
return lhs.store_iter != rhs.store_iter;
|
|
}
|
|
friend bool operator==(ConstIterator const &lhs, Iterator const &rhs) {
|
|
return lhs == rhs.to_const();
|
|
}
|
|
friend bool operator!=(ConstIterator const &lhs, Iterator const &rhs) {
|
|
return lhs != rhs.to_const();
|
|
}
|
|
friend bool operator==(Iterator const &lhs, ConstIterator const &rhs) {
|
|
return lhs.to_const() == rhs;
|
|
}
|
|
friend bool operator!=(Iterator const &lhs, ConstIterator const &rhs) {
|
|
return lhs.to_const() != rhs;
|
|
}
|
|
};
|
|
class ConstIterator {
|
|
public:
|
|
friend class Map<Key_T, Mapped_T>;
|
|
friend class Iterator;
|
|
using underlying = Iterator;
|
|
|
|
private:
|
|
underlying store_iter;
|
|
ConstIterator(underlying iter) : store_iter{iter} {}
|
|
|
|
public:
|
|
ConstIterator() = delete;
|
|
friend bool operator==(ConstIterator const &lhs, ConstIterator const &rhs) {
|
|
return lhs.store_iter == rhs.store_iter;
|
|
}
|
|
ConstIterator &operator++() {
|
|
++this->store_iter;
|
|
return *this;
|
|
}
|
|
ConstIterator operator++(int) {
|
|
ConstIterator tmp = *this;
|
|
this->store_iter++;
|
|
return tmp;
|
|
}
|
|
ConstIterator &operator--() {
|
|
--this->store_iter;
|
|
return *this;
|
|
}
|
|
ConstIterator operator--(int) {
|
|
ConstIterator tmp = *this;
|
|
this->store_iter--;
|
|
return tmp;
|
|
}
|
|
const ValueType &operator*() const { return *this->store_iter; }
|
|
const ValueType *operator->() const {
|
|
// I find this rather funny
|
|
return this->store_iter.operator->();
|
|
}
|
|
friend bool operator!=(ConstIterator const &lhs, ConstIterator const &rhs) {
|
|
return lhs.store_iter != rhs.store_iter;
|
|
}
|
|
};
|
|
class ReverseIterator {
|
|
public:
|
|
friend class Map<Key_T, Mapped_T>;
|
|
using underlying = typename std::vector<std::optional<ValueType>>::iterator;
|
|
|
|
private:
|
|
underlying store_iter;
|
|
|
|
public:
|
|
ReverseIterator() = delete;
|
|
ReverseIterator(underlying store_iter) : store_iter{store_iter} {}
|
|
ReverseIterator &operator++() {}
|
|
ReverseIterator operator++(int) {}
|
|
ReverseIterator &operator--() {}
|
|
ReverseIterator operator--(int) {}
|
|
ValueType &operator*() const {}
|
|
ValueType *operator->() const {}
|
|
friend bool operator==(ReverseIterator const &lhs,
|
|
ReverseIterator const &rhs) {
|
|
return lhs.store_iter == rhs.store_iter;
|
|
}
|
|
friend bool operator!=(ConstIterator const &lhs, ConstIterator const &rhs) {
|
|
return lhs.store_iter != rhs.store_iter;
|
|
}
|
|
};
|
|
Map() : store{} {}
|
|
Map(const Map &rhs) : store{rhs.store} {}
|
|
Map &operator=(const Map &rhs) { this->store = rhs.store; }
|
|
Map(std::initializer_list<ValueType> elems) : store{} {
|
|
this->insert(elems.begin(), elems.end());
|
|
}
|
|
// who cares we're using vector
|
|
~Map() {}
|
|
|
|
size_t size() const {
|
|
std::size_t count = 0;
|
|
for (auto &m_pair : this->store) {
|
|
count += m_pair.has_value() ? 1 : 0;
|
|
}
|
|
return count;
|
|
}
|
|
bool empty() const { return this->store.empty(); }
|
|
// TODO: Iterator creation
|
|
Iterator begin() {}
|
|
Iterator end() {}
|
|
ConstIterator begin() const {}
|
|
ConstIterator end() const {}
|
|
ConstIterator cbegin() const {}
|
|
ConstIterator cend() const {}
|
|
ReverseIterator rbegin() {}
|
|
ReverseIterator rend() {}
|
|
// TODO: actually return an iterator from find and deal with error cases
|
|
// correctly, also need to update for new bookkeeping type
|
|
Iterator find(const Key_T &key) {
|
|
std::size_t idx = 0;
|
|
while (store[idx].first != key) {
|
|
if (idx >= store.size()) {
|
|
return this->end();
|
|
}
|
|
if (store[idx].first < key) {
|
|
idx = idx * 2 + 1;
|
|
} else {
|
|
idx = idx * 2 + 2;
|
|
}
|
|
}
|
|
}
|
|
ConstIterator find(const Key_T &key) const {
|
|
std::size_t idx = 0;
|
|
while (store[idx].first != key) {
|
|
if (idx >= store.size()) {
|
|
return this->end();
|
|
}
|
|
if (store[idx].first < key) {
|
|
idx = idx * 2 + 1;
|
|
} else {
|
|
idx = idx * 2 + 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
Mapped_T &at(const Key_T &key) {
|
|
std::size_t i = 0;
|
|
while (this->store.at(i).has_value()) {
|
|
switch (true) {
|
|
case this->store.at(i).first == key:
|
|
return this->store.at(i).second;
|
|
case this->store.at(i).first < key:
|
|
i = 2 * i + 1;
|
|
break;
|
|
case this->store.at(i).first > key:
|
|
i = 2 * i + 2;
|
|
break;
|
|
}
|
|
}
|
|
throw std::out_of_range{""};
|
|
}
|
|
const Mapped_T &at(const Key_T &key) const { return this->at(key); }
|
|
Mapped_T &operator[](const Key_T &key) { return this->at(key); }
|
|
|
|
private:
|
|
Color getColor(std::size_t i) {
|
|
if (this->store.size() <= i) {
|
|
return Color::Black;
|
|
}
|
|
if (!this->store.at(i).has_value()) {
|
|
return Color::Black;
|
|
}
|
|
return this->store.at(i).value().color;
|
|
}
|
|
std::size_t find_null(const Key_T &key) {
|
|
std::size_t idx = 0;
|
|
while (store[idx].first != key) {
|
|
if (idx >= store.size()) {
|
|
return this->end();
|
|
}
|
|
if (store[idx].first < key) {
|
|
if (idx * 2 + 1 > store.size() || !store.at(idx * 2 + 1).has_value()) {
|
|
idx = idx * 2 + 1;
|
|
break;
|
|
}
|
|
idx = idx * 2 + 1;
|
|
} else {
|
|
if (idx * 2 + 2 > store.size() || !store.at(idx * 2 + 2).has_value()) {
|
|
idx = idx * 2 + 2;
|
|
break;
|
|
}
|
|
idx = idx * 2 + 2;
|
|
}
|
|
}
|
|
return idx;
|
|
}
|
|
enum class Direction { left, right };
|
|
void insert_helper(std::size_t idx, BookKeeping<Key_T, Mapped_T> to_insert) {
|
|
// might as well make sure
|
|
to_insert.color = Color::Red;
|
|
std::size_t parent_idx;
|
|
Direction relation;
|
|
if (idx % 2 == 1) {
|
|
parent_idx = (idx - 1) / 2;
|
|
relation = Direction::left;
|
|
} else {
|
|
parent_idx = (idx - 1) / 2;
|
|
relation = Direction::right;
|
|
}
|
|
BookKeeping<Key_T, Mapped_T> &parent = this->store.at(parent_idx).value();
|
|
}
|
|
|
|
public:
|
|
// TODO: single insert
|
|
// OH NO IT SHOULD BE RED-BLACK
|
|
std::pair<Iterator, bool> insert(const ValueType &val) {
|
|
|
|
BookKeeping<Key_T, Mapped_T> new_node;
|
|
new_node.color = Color::Red;
|
|
new_node.value = val;
|
|
new_node.parent = *this;
|
|
|
|
if (this->store.size() == 0) {
|
|
new_node.self = 0;
|
|
this->store.push_back(new_node);
|
|
}
|
|
}
|
|
template <typename IT_T> void insert(IT_T range_beg, IT_T range_end) {
|
|
std::for_each(range_beg, range_end,
|
|
[&](ValueType &val) { this->insert(val); });
|
|
}
|
|
// TODO: erase via iterator
|
|
void erase(Iterator pos) {
|
|
// RED BLACK TREE oh no
|
|
}
|
|
void erase(const Key_T &key) { this->erase(this->find(key)); }
|
|
void clear() { this->store = {}; }
|
|
friend bool operator==(const Map &lhs, const Map &rhs) {
|
|
if (lhs.store.size() != rhs.store.size()) {
|
|
return false;
|
|
}
|
|
auto liter = lhs.cbegin();
|
|
auto riter = rhs.cbegin();
|
|
// both must be the same length so this is fine
|
|
while (liter != lhs.cend()) {
|
|
if (*liter != *riter) {
|
|
return false;
|
|
}
|
|
liter++;
|
|
riter++;
|
|
}
|
|
return true;
|
|
}
|
|
friend bool operator!=(const Map &lhs, const Map &rhs) {
|
|
return !(lhs == rhs);
|
|
}
|
|
friend bool operator<(const Map &lhs, const Map &rhs) {
|
|
std::size_t lhs_i = 0;
|
|
std::size_t rhs_i = 0;
|
|
for (; lhs_i < lhs.store.size() && rhs_i < rhs.store.size();
|
|
lhs_i++, rhs_i++) {
|
|
bool lhs_exhaust = false;
|
|
while (!lhs.store[lhs_i].has_value()) {
|
|
lhs_i++;
|
|
if (lhs.store.size() >= lhs_i) {
|
|
lhs_exhaust = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool rhs_exhaust = false;
|
|
while (!rhs.store[rhs_i].has_value()) {
|
|
rhs_i++;
|
|
if (rhs.store.size() >= rhs_i) {
|
|
rhs_exhaust = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (lhs_exhaust && !rhs_exhaust) {
|
|
return true;
|
|
}
|
|
if (lhs_exhaust || rhs_exhaust) {
|
|
break;
|
|
}
|
|
if (lhs.store[lhs_i] != rhs.store[rhs_i]) {
|
|
return lhs.store[lhs_i] < rhs.store[rhs_i];
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
} // namespace cs440
|