diff --git a/dsalglib/CMakeLists.txt b/dsalglib/CMakeLists.txt index 30e4bd7..7aaf84f 100644 --- a/dsalglib/CMakeLists.txt +++ b/dsalglib/CMakeLists.txt @@ -17,6 +17,7 @@ set(SOURCE_FILES include/kmpsearch.h include/linklist.h include/matrix.h + include/memory/unique_ptr.h include/mergesort.h include/queue.h include/quicksort.h @@ -31,6 +32,6 @@ set(SOURCE_FILES main.cpp sample.h tests.h - timepass.h include/hashmap.cpp include/hashmap.h) + include/hashmap.cpp include/hashmap.h) -add_executable(dsalglib ${SOURCE_FILES}) \ No newline at end of file +add_executable(dsalglib ${SOURCE_FILES}) diff --git a/dsalglib/dsalglib.h b/dsalglib/dsalglib.h index bb632de..ae8d8ab 100644 --- a/dsalglib/dsalglib.h +++ b/dsalglib/dsalglib.h @@ -13,6 +13,7 @@ #include "include/search.h" #include "include/sort.h" #include "include/queue.h" +#include "include/reversaltree.h" #include "include/tree.h" #include "include/heap.h" #include "include/graph.h" diff --git a/dsalglib/include/alginc.h b/dsalglib/include/alginc.h index ed1fd62..a2bab3f 100644 --- a/dsalglib/include/alginc.h +++ b/dsalglib/include/alginc.h @@ -9,16 +9,49 @@ * contains fundamental functions required */ + +#define STR_DETAIL(x) #x +#define STR(x) STR_DETAIL(x) + +#if defined(DEBUG) +#define EXPECTS(cond) if (!(cond))\ +throw ("Precondition failure at " __FILE__ ":"\ + STR(__LINE__)); +#else +#define EXPECTS(cond) +#endif + namespace dsa { + template + struct remove_reference { + using type = T; + }; + template + struct remove_reference { + using type = T; + }; + template + struct remove_reference { + using type = T; + }; + + // Analogue of std::move + template + typename remove_reference::type&& + rvalue_cast(T&& t) noexcept + { + return static_cast::type&&>(t); + } + template inline void swapit(T &x, T &y) { - T t = x; - x = y; - y = t; + T t = rvalue_cast(x); + x = rvalue_cast(y); + y = rvalue_cast(t); } template @@ -37,5 +70,17 @@ namespace dsa return min; } + // Analogue of std::forward. + template + T&& forward(typename remove_reference::type& t) noexcept + { + return static_cast(t); + } + template + T&& forward(typename remove_reference::type&& t) noexcept + { + return static_cast(t); + } + } #endif //DSALGLIB_ALGINC_H diff --git a/dsalglib/include/memory/unique_ptr.h b/dsalglib/include/memory/unique_ptr.h new file mode 100644 index 0000000..7142f41 --- /dev/null +++ b/dsalglib/include/memory/unique_ptr.h @@ -0,0 +1,70 @@ +#ifndef DSALGLIB_UNIQUE_PTR_H +#define DSALGLIB_UNIQUE_PTR_H + +#include "../alginc.h" + +namespace dsa { + +template +class unique_ptr { +public: + unique_ptr(T* ptr = nullptr) : ptr_(ptr) {} + + ~unique_ptr() { + delete ptr_; + } + + unique_ptr(const unique_ptr&) = delete; + + unique_ptr(unique_ptr&& rhs) : ptr_(rhs.ptr_) { + rhs.ptr_ = nullptr; + } + + unique_ptr& operator=(const unique_ptr&) = delete; + + unique_ptr& operator=(unique_ptr&& rhs) { + // calling delete on nullptr is not an error + delete ptr_; + ptr_ = rhs.ptr_; + rhs.ptr_ = nullptr; + return *this; + } + + T* get() { + return ptr_; + } + + const T* get() const { + return ptr_; + } + + T& operator*() { + return *ptr_; + } + const T& operator*() const { + return *ptr_; + } + + T* operator->() { + return ptr_; + } + const T* operator->() const { + return ptr_; + } + + operator bool() const { + return ptr_ != nullptr; + } + +private: + T* ptr_; +}; + +template +unique_ptr make_unique(Args... args) { + return unique_ptr(new T(rvalue_cast(args)...)); +} + +} // namespace dsa + +#endif // DSALGLIB_UNIQUE_PTR_H diff --git a/dsalglib/include/reversaltree.h b/dsalglib/include/reversaltree.h new file mode 100644 index 0000000..908223a --- /dev/null +++ b/dsalglib/include/reversaltree.h @@ -0,0 +1,644 @@ +// Implementation of splay tree on implicit key. Emulates sequence of elements. +// Supports concatenating (merging), splitting and reversing sequences with +// log(n) complexity on average. + +#ifndef DSALGLIB_REVERSALTREE_H +#define DSALGLIB_REVERSALTREE_H + +#include "memory/unique_ptr.h" + + +namespace dsa { + +using size_t = unsigned int; + +template +T exchange(T& obj, U&& new_value) +{ + T old_value = rvalue_cast(obj); + obj = forward(new_value); + return old_value; +} + +template +struct conditional { + using type = T; +}; + +template +struct conditional { + using type = F; +}; + +template +using conditional_t = typename conditional::type; + +template +size_t iterator_distance(ForwardIterator first, ForwardIterator last) { + size_t result = 0; + while (first != last) { + ++first; + ++result; + } + return result; +} + +template +ForwardIterator advance(ForwardIterator it, size_t steps) { + for (size_t i = 0; i < steps; ++i) { + ++it; + } + return it; +} + +template +class ReversalTree { + public: + template + class Iterator; + + using value_type = T; + using size_type = size_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = Iterator; + using const_iterator = Iterator; + + private: + class Node; + + public: + ReversalTree() {} + ReversalTree(const ReversalTree&) = delete; + ReversalTree(ReversalTree&&); + + template + ReversalTree(ForwardIterator begin, ForwardIterator end); + + ReversalTree& operator=(const ReversalTree&) = delete; + ReversalTree& operator=(ReversalTree&&); + + bool empty() const noexcept { + return !root_ptr(); + } + + size_type size() const noexcept { + return dummy_.leftSubtreeSize(); + } + + void clear(); + + // Return reference to element at given position. + // Average complexity is O(log size()). + // Recently accessed elements are accessed faster. + reference at(size_type index); + const_reference at(size_type index) const; + + reference front() { + return at(0); + } + const_reference front() const { + return at(0); + } + + reference back() { + return at(size() - 1); + } + const_reference back() const { + return at(size() - 1); + } + + // Remove elements in [left_size, size()) and return new tree with those + // elements. + // Complexity: O(log size()) + ReversalTree split(size_type left_size); + + // Split before given iterator. + ReversalTree split(const_iterator); + + // Append elements from rhs to this tree. + // Complexity: O(log size()) + void merge(ReversalTree&& rhs); + + // Reverse elements in range [first, last) + // Complexity: O(log size()) + void reverse(size_type first, size_type last); + + iterator insert(const_iterator, T); + + iterator erase(const_iterator pos); + iterator erase(const_iterator first, const_iterator last); + + void swap(ReversalTree& rhs) noexcept; + + // Iterating doesn't change tree structure. + iterator begin(); + const_iterator begin() const; + const_iterator cbegin() const; + iterator end(); + const_iterator end() const; + const_iterator cend() const; + + private: + Node& root() const { + return *(dummy_.son_[0]); + } + + unique_ptr& root_ptr() const { + return dummy_.son_[0]; + } + + bool isRoot(const Node& node) const { + return node.dad_ == node.dad_->dad_; + } + + template + unique_ptr buildTree(ForwardIterator, ForwardIterator); + template + unique_ptr buildTree(ForwardIterator, ForwardIterator, size_t dist); + + void rotate(Node&); + + // Move node up making it root while rebalancing nodes on its path. + void splay(Node&); + + Node& findNode(size_type index) const; + + void reverseTree() noexcept; + + private: + mutable Node dummy_; +}; + +template +class ReversalTree::Node { + public: + explicit Node(T data = T()) : data_(rvalue_cast(data)) { + dad_ = this; + } + Node(const Node&) = delete; + Node(Node&&) = default; + + bool whichSon() const noexcept { + return dad_->son_[1].get() == this; + } + + ReversalTree::size_type leftSubtreeSize() const noexcept { + return son_[0] ? son_[0]->subtree_size_ : 0; + } + + void updateSubtreeSize() noexcept { + subtree_size_ = 1; + for (bool dir : {0, 1}) { + if (son_[dir]) { + subtree_size_ += son_[dir]->subtree_size_; + } + } + } + + void push() { + if (reverse_) { + swapit(son_[0], son_[1]); + for (bool dir : {0, 1}) { + if (son_[dir]) { + son_[dir]->reverse_ ^= 1; + } + } + reverse_ = false; + } + } + + void link(unique_ptr&& son, bool dir) { + son_[dir] = rvalue_cast(son); + if (son_[dir]) { + son_[dir]->dad_ = this; + } + updateSubtreeSize(); + } + + public: + T data_; + unique_ptr son_[2]; + Node* dad_; + typename ReversalTree::size_type subtree_size_ = 1; + bool reverse_ = false; +}; + +template +template +class ReversalTree::Iterator { + friend class ReversalTree; + public: + using value_type = ReversalTree::value_type; + using reference = conditional_t; + using pointer = conditional_t; + + public: + Iterator() : node_(nullptr) {} + Iterator(const Iterator& rhs) { + node_ = rhs.node_; + } + + // Average complexity for increment / decrement operators is O(1) + // Worst case complexity is O(height) + Iterator& operator++(); + + Iterator operator++(int) { + auto result = *this; + operator++(); + return result; + } + + Iterator& operator--(); + + Iterator operator--(int) { + auto result = *this; + operator--(); + return result; + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { + return lhs.node_ == rhs.node_; + } + + friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { + return !operator==(lhs, rhs); + } + + reference operator*() const { + return node_->data_; + } + + pointer operator->() const { + return &node_->data_; + } + + // Return index in containing ReversalTree. + typename ReversalTree::size_type order() const; + + private: + Iterator(ReversalTree::Node* node) : node_(node) {} + + private: + ReversalTree::Node* node_; +}; + + + +template +ReversalTree::ReversalTree(ReversalTree&& rhs) { + swap(rhs); +} + +template +ReversalTree& ReversalTree::operator=(ReversalTree&& rhs) { + swap(rhs); + return *this; +} + +template +template +ReversalTree::ReversalTree(ForwardIterator begin, ForwardIterator end) { + dummy_.link(buildTree(begin, end), 0); +} + +template +template +auto ReversalTree::buildTree( + ForwardIterator begin, + ForwardIterator end, + size_t dist +) -> unique_ptr { + if (dist == 0) { + return nullptr; + } + if (dist == 1) { + return make_unique(*begin); + } + const auto mid_pos = dist / 2; + auto mid = advance(begin, mid_pos); + auto result = make_unique(*mid); + result->link(buildTree(begin, mid, mid_pos), 0); + result->link(buildTree(++mid, end, dist - mid_pos - 1), 1); + return result; +} +template +template +auto ReversalTree::buildTree( + ForwardIterator begin, + ForwardIterator end +) -> unique_ptr { + return buildTree(begin, end, iterator_distance(begin, end)); +} + +template +void ReversalTree::clear() { + root_ptr() = nullptr; + dummy_.updateSubtreeSize(); +} + +template +void ReversalTree::swap(ReversalTree& rhs) noexcept { + swapit(rhs.root_ptr(), root_ptr()); + if (root_ptr()) { + root_ptr()->dad_ = &dummy_; + } + if (rhs.root_ptr()) { + rhs.root_ptr()->dad_ = &rhs.dummy_; + } + dummy_.updateSubtreeSize(); + rhs.dummy_.updateSubtreeSize(); +} + +template +void ReversalTree::rotate(Node& u) { + if (&u == &dummy_ || isRoot(u)) { + throw ("Attempt to rotate root"); + } + Node& v = *u.dad_; + Node& w = *v.dad_; + const bool dir = u.whichSon(); + const bool dad_dir = v.whichSon(); + u.son_[!dir] = exchange(w.son_[dad_dir], exchange(v.son_[dir], + exchange(u.son_[!dir], nullptr))); + if (v.son_[dir]) { + v.son_[dir]->dad_ = &v; + } + v.dad_ = &u; + u.dad_ = &w; + v.updateSubtreeSize(); + u.updateSubtreeSize(); + w.updateSubtreeSize(); +} + +template +void ReversalTree::splay(Node& u) { + EXPECTS(&u != &dummy_) + while (!isRoot(u)) { + Node& v = *u.dad_; + if (isRoot(v)) { + v.push(); + u.push(); + rotate(u); + } else { + v.dad_->push(); + v.push(); + u.push(); + const bool u_dir = u.whichSon(); + const bool v_dir = v.whichSon(); + if (u_dir == v_dir) { + rotate(v); + rotate(u); + } else { + rotate(u); + rotate(u); + } + } + } +} + +template +auto ReversalTree::findNode(size_type i) const -> Node& { + size_type cumulated = 0; + Node* p = root_ptr().get(); + while (true) { + p->push(); + const size_type index = cumulated + p->leftSubtreeSize(); + if (index == i) { + return *p; + } + if (i < index) { + p = p->son_[0].get(); + } else { + p = p->son_[1].get(); + cumulated = index + 1; + } + } +} + +template +auto ReversalTree::at(size_type i) -> reference { + if (i >= size()) { + throw "at(): index out of range"; + } + splay(findNode(i)); + return root().data_; +} + +template +auto ReversalTree::at(size_type i) const -> const_reference { + if (i >= size()) { + throw "at(): index out of range"; + } + return findNode(i).data_; +} + +template +ReversalTree ReversalTree::split(size_type left_size) { + return split(const_iterator(&findNode(left_size))); +} + +template +ReversalTree ReversalTree::split(const_iterator it) { + auto prev = it; + try { + --prev; + } catch (...) { + return rvalue_cast(*this); + } + splay(*prev.node_); + ReversalTree result; + result.dummy_.link(rvalue_cast(root().son_[1]), 0); + root().updateSubtreeSize(); + return result; +} + +template +void ReversalTree::merge(ReversalTree&& rhs) { + if (empty()) { + swap(rhs); + return; + } + if (rhs.empty()) { + return; + } + splay(findNode(size() - 1)); + root().link(rvalue_cast(rhs.dummy_.son_[0]), 1); +} + +template +void ReversalTree::reverseTree() noexcept { + if (!empty()) { + root().reverse_ ^= true; + } +} + +template +void ReversalTree::reverse(size_type first, size_type last) { + if (first == 0 && last == size()) { + reverseTree(); + return; + } + if (first > last) { + throw "reverse(): first is greater than last"; + } + if (last > size()) { + throw "reverse(): last is greater than size"; + } + auto right = split(last); + auto center = split(first); + center.reverseTree(); + merge(rvalue_cast(center)); + merge(rvalue_cast(right)); +} + +template +auto ReversalTree::insert(const_iterator it, T e) -> iterator { + Node* new_node = nullptr; + if (!it.node_->son_[0]) { + it.node_->link(make_unique(rvalue_cast(e)), 0); + new_node = it.node_->son_[0].get(); + } else { + Node* p = it.node_->son_[0].get(); + while (p->son_[1]) { + p = p->son_[1].get(); + } + p->link(make_unique(rvalue_cast(e)), 1); + new_node = p->son_[1].get(); + } + splay(*new_node); + return iterator{new_node}; +} + +template +auto ReversalTree::erase(const_iterator pos) -> iterator { + const auto copy = pos; + return erase(copy, ++pos); +} + +template +auto ReversalTree::erase(const_iterator first, + const_iterator last) -> iterator { + auto right = split(last); + split(first); + merge(rvalue_cast(right)); + if (empty() || !root().son_[1]) { + return end(); + } else { + return iterator(root().son_[1].get()); + } +} + +template +auto ReversalTree::begin() -> iterator { + if (empty()) { + return end(); + } + return iterator(&findNode(0)); +} + +template +auto ReversalTree::begin() const -> const_iterator { + if (empty()) { + return end(); + } + return const_iterator(&findNode(0)); +} + +template +auto ReversalTree::cbegin() const -> const_iterator { + if (empty()) { + return cend(); + } + return begin(); +} + +template +auto ReversalTree::end() -> iterator { + return iterator(&dummy_); +} + +template +auto ReversalTree::end() const -> const_iterator { + return const_iterator(&dummy_); +} + +template +auto ReversalTree::cend() const -> const_iterator { + return end(); +} + + +template +template +typename ReversalTree::template Iterator& +ReversalTree::Iterator::operator++() { + if (node_->dad_ == node_) { + throw ("Calling operator++ for end iterator"); + } + node_->push(); + if (node_->son_[1]) { + node_ = node_->son_[1].get(); + node_->push(); + while (node_->son_[0]) { + node_ = node_->son_[0].get(); + node_->push(); + } + } else { + while (node_->whichSon()) { + node_ = node_->dad_; + } + node_ = node_->dad_; + } + return *this; +} + +template +template +typename ReversalTree::template Iterator& +ReversalTree::Iterator::operator--() { + node_->push(); + if (node_->son_[0]) { + node_ = node_->son_[0].get(); + node_->push(); + while (node_->son_[1]) { + node_ = node_->son_[1].get(); + node_->push(); + } + } else { + while (!node_->whichSon()) { + node_ = node_->dad_; + if (node_->dad_ == node_) { + throw ("Calling operator-- for begin iterator"); + } + } + node_ = node_->dad_; + } + return *this; +} + +template +template +auto ReversalTree::Iterator::order() const -> size_type { + size_type result = node_->leftSubtreeSize(); + Node* p = node_; + while (p->dad_ != p->dad_->dad_) { + if (p->whichSon()) { + p = p->dad_; + result += p->leftSubtreeSize() + 1; + } else { + p = p->dad_; + } + } + return result; +} + +} //namespace dsa + +#endif //DSALGLIB_REVERSALTREE_H diff --git a/dsalglib/tests.h b/dsalglib/tests.h index b479dd7..4f5cb7c 100644 --- a/dsalglib/tests.h +++ b/dsalglib/tests.h @@ -7,6 +7,10 @@ #include #include "dsalglib.h" #include "sample.h" + +#define ASSERT(cond) if (!(cond))\ + throw ("Assertion failed at " __FILE__ ":" STR(__LINE__)); + using namespace std; using namespace dsa; @@ -249,4 +253,45 @@ void trie_test(){ cout << t.search("third") << "\n"; } +void reversalTreeTest() { + const int numbers[] = {2, 3, 4, 5, 6, 7, 8}; + const int n = sizeof(numbers) / sizeof(int); + ReversalTree tree(numbers, numbers + n); + ASSERT(tree.at(0) == 2); + ASSERT(tree.at(5) == 7); + + tree.reverse(2, 5); + tree.reverse(0, 4); + { + const int expected[] = {5, 6, 3, 2, 4, 7, 8}; + ASSERT(tree.size() == n); + auto it = tree.cbegin(); + for (int i = 0; i < n; ++i, ++it) { + ASSERT(*it == expected[i]); + } + ASSERT(it == tree.cend()); + } + + tree.erase(--tree.end()); + tree.insert(tree.begin(), 1); + ASSERT(tree.front() == 1); + ASSERT(tree.back() == 7); + ASSERT(tree.size() == n); + // {1, 5, 6, 3, 2, 4, 7} + + ReversalTree::iterator mid = advance(tree.begin(), n / 2); + ReversalTree other = tree.split(mid); + other.merge(rvalue_cast(tree)); + { + const int expected[] = {3, 2, 4, 7, 1, 5, 6}; + ASSERT(other.size() == n); + auto it = other.cbegin(); + for (int i = 0; i < n; ++i, ++it) { + ASSERT(*it == expected[i]); + } + } + ASSERT(tree.empty()); + tree.clear(); +} + #endif //DSALGLIB_TESTS_H