diff --git a/lib/glacier/container/binary_tree.h b/lib/glacier/container/binary_tree.h new file mode 100644 index 0000000..08bf80b --- /dev/null +++ b/lib/glacier/container/binary_tree.h @@ -0,0 +1,173 @@ +#pragma once + +#include "glacier/container/optional.h" +#include "glacier/container/pair.h" +#include "glacier/memory/move.h" +#include "glacier/memory/ref_counted.h" +#include "glacier/memory/ref_ptr.h" +#include "glacier/memory/reference.h" +#include "glacier/memory/unique_ptr.h" + +namespace glcr { + +template +class BinaryTree { + public: + BinaryTree() = default; + BinaryTree(const BinaryTree&) = delete; + // FIXME: Implement move. + BinaryTree(BinaryTree&&) = delete; + + void Insert(K key, V&& value); + void Delete(K key); + + Optional> Predecessor(K key); + Optional> Successor(K key); + + Optional> Find(K key); + + private: + // TODO: Consider adding a sharedptr type to + // avoid making this "RefCounted". + struct BinaryNode : public RefCounted { + K key; + V value; + + RefPtr left; + RefPtr right; + RefPtr parent; + + BinaryNode(K k, V v) : key(k), value(Move(v)) {} + }; + + RefPtr root_; + + // If this node exists, return it. Otherwise, this + // will be the parent of where this node would be inserted. + RefPtr FindOrInsertionParent(K key); +}; + +template +void BinaryTree::Insert(K key, V&& value) { + auto parent = FindOrInsertionParent(key); + if (parent.empty()) { + root_ = AdoptPtr(new BinaryNode(key, Move(value))); + return; + } + + if (parent->key > key) { + parent->left = AdoptPtr(new BinaryNode(key, Move(value))); + parent->left->parent = parent; + } else if (parent->key < key) { + parent->right = AdoptPtr(new BinaryNode(key, Move(value))); + parent->right->parent = parent; + } else { + parent->value = Move(value); + } +} + +template +void BinaryTree::Delete(K key) { + // TODO: Implement Delete. + return; +} + +template +Optional> BinaryTree::Predecessor(K key) { + auto current = FindOrInsertionParent(key); + + // The case where the current is the insertion parent and + // the predecessor is unique. If the key was going to be + // inserted as the left child, it shares its predecessor with the parent. + if (current->key < key) { + return Optional>(current->value); + } + + // Case where the predecessor is below us in the tree. + if (current->left) { + current = current->left; + while (current->right) { + current = current->right; + } + return {current->value}; + } + + // Case where the predecessor is above us in the tree. + auto parent = current->parent; + while (parent && (parent->left == current)) { + current = parent; + parent = current->parent; + } + if (parent) { + return {parent->value}; + } + return {}; +} + +template +Optional> BinaryTree::Successor(K key) { + auto current = FindOrInsertionParent(key); + + // The case where the current is the insertion parent and + // the predecessor is unique. If the key was going to be + // inserted as the left child, it shares its predecessor with the parent. + if (current->key > key) { + return Optional>(current->value); + } + + // Case where the predecessor is below us in the tree. + if (current->right) { + current = current->right; + while (current->left) { + current = current->left; + } + return {current->value}; + } + + // Case where the predecessor is above us in the tree. + auto parent = current->parent; + while (parent && (parent->right == current)) { + current = parent; + parent = current->parent; + } + if (parent) { + return {parent->value}; + } + return {}; +} + +template +Optional> BinaryTree::Find(K key) { + auto current = FindOrInsertionParent(key); + if (current->key == key) { + return Optional>(current->value); + } + return {}; +} + +template +RefPtr::BinaryNode> +BinaryTree::FindOrInsertionParent(K key) { + if (root_.empty()) { + return nullptr; + } + + auto current = root_; + while (true) { + if (key == current->key) { + return current; + } else if (key < current->key) { + if (!current->left) { + return current; + } + current = current->left; + } else { + if (!current->right) { + return current; + } + current = current->right; + } + } +} + +} // namespace glcr diff --git a/lib/glacier/container/optional.h b/lib/glacier/container/optional.h new file mode 100644 index 0000000..a847ad9 --- /dev/null +++ b/lib/glacier/container/optional.h @@ -0,0 +1,41 @@ +#pragma once + +#include "glacier/memory/move.h" + +namespace glcr { + +template +class Optional { + public: + Optional() : empty_(nullptr), has_value_(false) {} + Optional(const T& value) : value_(value), has_value_(true) {} + Optional(T&& value) : value_(Move(value)), has_value_(true) {} + Optional(const Optional&) = default; + Optional(Optional&&) = default; + ~Optional() { + if (has_value_) { + value_.~T(); + } + } + + bool empty() const { return !has_value_; } + explicit operator bool() { return has_value_; } + + const T& value() const { return value_; } + T&& release_value() { + has_value_ = false; + return Move(value_); + } + + T* operator->() { return &value_; } + T& operator*() { return value_; } + + private: + union { + T value_; + void* empty_; + }; + bool has_value_; +}; + +} // namespace glcr diff --git a/lib/glacier/memory/ref_ptr.h b/lib/glacier/memory/ref_ptr.h index 6cdba6f..2ed2ceb 100644 --- a/lib/glacier/memory/ref_ptr.h +++ b/lib/glacier/memory/ref_ptr.h @@ -51,6 +51,8 @@ class RefPtr { T* get() const { return ptr_; }; T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } + + bool empty() const { return ptr_ == nullptr; } operator bool() const { return ptr_ != nullptr; } bool operator==(decltype(nullptr)) const { return (ptr_ == nullptr); } diff --git a/lib/glacier/memory/reference.h b/lib/glacier/memory/reference.h new file mode 100644 index 0000000..5ed4bfa --- /dev/null +++ b/lib/glacier/memory/reference.h @@ -0,0 +1,18 @@ +#pragma once + +namespace glcr { + +template +class Ref { + public: + Ref(T& ref) : ref_(ref) {} + Ref(const Ref& other) = default; + Ref(Ref&& other) = default; + + operator T&() const { return ref_; } + + private: + T& ref_; +}; + +} // namespace glcr