From 7b30edc8c886f0d88bb70c41adcb957de0adf22e Mon Sep 17 00:00:00 2001 From: ZhuangYumin Date: Mon, 15 Apr 2024 03:58:17 +0000 Subject: [PATCH] manually integrate stlite --- stlite/exceptions.hpp | 39 ++ stlite/list.hpp | 429 ++++++++++++++++++++ stlite/map.hpp | 803 ++++++++++++++++++++++++++++++++++++++ stlite/priority_queue.hpp | 129 ++++++ stlite/utility.hpp | 27 ++ stlite/vector.hpp | 543 ++++++++++++++++++++++++++ 6 files changed, 1970 insertions(+) create mode 100644 stlite/exceptions.hpp create mode 100644 stlite/list.hpp create mode 100644 stlite/map.hpp create mode 100644 stlite/priority_queue.hpp create mode 100644 stlite/utility.hpp create mode 100644 stlite/vector.hpp diff --git a/stlite/exceptions.hpp b/stlite/exceptions.hpp new file mode 100644 index 0000000..183eb41 --- /dev/null +++ b/stlite/exceptions.hpp @@ -0,0 +1,39 @@ +#ifndef SJTU_EXCEPTIONS_HPP +#define SJTU_EXCEPTIONS_HPP + +#include +#include +#include + +namespace sjtu { + +class exception { +protected: + const std::string variant = ""; + std::string detail = ""; +public: + exception() {} + exception(const exception &ec) : variant(ec.variant), detail(ec.detail) {} + virtual std::string what() { + return variant + " " + detail; + } +}; + +class index_out_of_bound : public exception { + /* __________________________ */ +}; + +class runtime_error : public exception { + /* __________________________ */ +}; + +class invalid_iterator : public exception { + /* __________________________ */ +}; + +class container_is_empty : public exception { + /* __________________________ */ +}; +} + +#endif diff --git a/stlite/list.hpp b/stlite/list.hpp new file mode 100644 index 0000000..c7dbeb4 --- /dev/null +++ b/stlite/list.hpp @@ -0,0 +1,429 @@ +#ifndef SJTU_LIST_HPP +#define SJTU_LIST_HPP + +#include +#include +namespace sjtu { +/** + * a data container like std::list + * allocate random memory addresses for data and they are doubly-linked in a + * list. + */ +templateclass list { +protected: + + class node { +public: + + /** + * add data members and constructors & destructor + */ + T val; + node *prev, *next; + node() : prev(nullptr), next(nullptr) {} + + node(const T &val, node *prev = nullptr, node *next = nullptr) : + val(val), prev(prev), next(next) {} + }; + +protected: + + /** + * add data members for linked list as protected members + */ + node *head, *tail; + int element_count; + + /** + * insert node cur before node pos + * return the inserted node cur + */ + node* insert(node *pos, node *cur) { + if (pos->prev != nullptr) pos->prev->next = cur; + cur->prev = pos->prev; + cur->next = pos; + pos->prev = cur; + return cur; + } + + /** + * remove node pos from list (no need to delete the node) + * return the removed node pos + */ + node* erase(node *pos) { + if (pos->prev != nullptr) pos->prev->next = pos->next; + if (pos->next != nullptr) pos->next->prev = pos->prev; + return pos; + } + +public: + + class const_iterator; + class iterator { +private: + + /** + * TODO add data members + * just add whatever you want. + */ + +public: + + bool is_end; + node *cur; + iterator() : is_end(true), cur(nullptr) {} + + iterator(node *cur, bool is_end = false) : is_end(is_end), cur(cur) {} + + iterator operator++(int) { + iterator res = *this; + ++(*this); + return res; + } + + iterator& operator++() { + if (cur != nullptr) { + if (cur->next != nullptr) cur = cur->next; + else is_end = true; + } + return *this; + } + + iterator operator--(int) { + iterator res = *this; + --(*this); + return res; + } + + iterator& operator--() { + if (is_end) is_end = false; + else if (cur != nullptr) cur = cur->prev; + return *this; + } + + /** + * TODO *it + * throw std::exception if iterator is invalid + */ + T& operator*() const { + if ((cur == nullptr) || is_end) throw std::exception(); + return cur->val; + } + + /** + * TODO it->field + * throw std::exception if iterator is invalid + */ + T * operator->() const noexcept { + if ((cur == nullptr) || is_end) throw std::exception(); + return &(cur->val); + } + + /** + * a operator to check whether two iterators are same (pointing to the same + * memory). + */ + bool operator==(const iterator &rhs) const { + return cur == rhs.cur && is_end == rhs.is_end; + } + + bool operator==(const const_iterator &rhs) const { + return cur == rhs.cur && is_end == rhs.is_end; + } + + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { + return cur != rhs.cur || is_end != rhs.is_end; + } + + bool operator!=(const const_iterator &rhs) const { + return cur != rhs.cur || is_end != rhs.is_end; + } + }; + + /** + * TODO + * has same function as iterator, just for a const object. + * should be able to construct from an iterator. + */ + class const_iterator { +private: + + bool is_end; + node *cur; + + /** + * TODO add data members + * just add whatever you want. + */ + +public: + + const_iterator() : is_end(true), cur(nullptr) {} + + const_iterator(const iterator &other) : is_end(other.is_end), + cur(other.cur) {} + + const_iterator(node *cur, bool is_end = false) : is_end(is_end), + cur(cur) {} + + const_iterator operator++(int) { + const_iterator res = *this; + ++(*this); + return res; + } + + const_iterator& operator++() { + if (cur != nullptr) { + if (cur->next != nullptr) cur = cur->next; + else is_end = true; + } + return *this; + } + + const_iterator operator--(int) { + const_iterator res = *this; + --(*this); + return res; + } + + const_iterator& operator--() { + if (is_end) is_end = false; + else if (cur != nullptr) cur = cur->prev; + return *this; + } + + /** + * TODO *it + * throw std::exception if iterator is invalid + */ + const T& operator*() const { + if ((cur == nullptr) || is_end) throw std::exception(); + return cur->val; + } + + /** + * TODO it->field + * throw std::exception if iterator is invalid + */ + const T * operator->() const noexcept { + if ((cur == nullptr) || is_end) throw std::exception(); + return &(cur->val); + } + + /** + * a operator to check whether two iterators are same (pointing to the same + * memory). + */ + bool operator==(const iterator &rhs) const { + return cur == rhs.cur && is_end == rhs.is_end; + } + + bool operator==(const const_iterator &rhs) const { + return cur == rhs.cur && is_end == rhs.is_end; + } + + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { + return cur != rhs.cur || is_end != rhs.is_end; + } + + bool operator!=(const const_iterator &rhs) const { + return cur != rhs.cur || is_end != rhs.is_end; + } + }; + + /** + * TODO Constructs + * Atleast two: default constructor, copy constructor + */ + list() : head(nullptr), tail(nullptr), element_count(0) {} + + void copy(const list &other) { + head = nullptr; + tail = nullptr; + element_count = 0; + if (other.element_count != 0) { + head = tail = new node(); + head->val = other.head->val; + node *p = other.head->next; + while (p != nullptr) { + tail->next = new node(p->val, tail); + tail = tail->next; + p = p->next; + } + element_count = other.element_count; + } + } + + list(const list &other) { + copy(other); + } + + /** + * TODO Destructor + */ + virtual ~list() { + clear(); + } + + /** + * TODO Assignment operator + */ + list& operator=(const list &other) { + if (this == &other) return *this; + clear(); + copy(other); + return *this; + } + + /** + * access the first / last element + * throw container_is_empty when the container is empty. + */ + const T& front() const { + if (element_count == 0) throw std::exception(); + return head->val; + } + + const T& back() const { + if (element_count == 0) throw std::exception(); + return tail->val; + } + + /** + * returns an iterator to the beginning. + */ + iterator begin() { + return iterator(head, head == nullptr); + } + + const_iterator cbegin() const { + return const_iterator(head, head == nullptr); + } + + /** + * returns an iterator to the end. + */ + iterator end() { + return iterator(tail, true); + } + + const_iterator cend() const { + return const_iterator(tail, true); + } + + /** + * checks whether the container is empty. + */ + virtual bool empty() const { + return element_count == 0; + } + + /** + * returns the number of elements + */ + virtual size_t size() const { + return element_count; + } + + /** + * clears the contents + */ + virtual void clear() { + node *p = head; + while (p != nullptr) { + node *p_nxt = p->next; + delete p; + p = p_nxt; + } + head = nullptr; + tail = nullptr; + element_count = 0; + } + + /** + * insert value before pos (pos may be the end() iterator) + * return an iterator pointing to the inserted value + * throw if the iterator is invalid + */ + virtual iterator insert(iterator pos, const T &value) { + if (!pos.is_end) { + node *cur = new node(value); + insert(pos.cur, cur); + if (pos.cur == head) head = cur; + element_count++; + return iterator(cur); + } else { + if (element_count == 0) { + head = tail = new node(value); + element_count++; + return iterator(tail); + } + tail->next = new node(value, tail); + tail = tail->next; + element_count++; + return iterator(tail); + } + } + + /** + * remove the element at pos (the end() iterator is invalid) + * returns an iterator pointing to the following element, if pos pointing to + * the last element, end() will be returned. throw if the container is empty, + * the iterator is invalid + */ + virtual iterator erase(iterator pos) { + if ((element_count == 0) || pos.is_end) throw std::exception(); + iterator res = pos; + ++res; + if (pos.cur == head) { + head = head->next; + } + if (pos.cur == tail) { + tail = tail->prev; + } + delete erase(pos.cur); + element_count--; + return res; + } + + /** + * adds an element to the end + */ + void push_back(const T &value) { + insert(end(), value); + } + + /** + * removes the last element + * throw when the container is empty. + */ + void pop_back() { + if (element_count == 0) throw std::exception(); + erase(iterator(tail)); + } + + /** + * inserts an element to the beginning. + */ + void push_front(const T &value) { + insert(begin(), value); + } + + /** + * removes the first element. + * throw when the container is empty. + */ + void pop_front() { + if (element_count == 0) throw std::exception(); + erase(iterator(head)); + } +}; +} // namespace sjtu + +#endif // SJTU_LIST_HPP \ No newline at end of file diff --git a/stlite/map.hpp b/stlite/map.hpp new file mode 100644 index 0000000..59366a0 --- /dev/null +++ b/stlite/map.hpp @@ -0,0 +1,803 @@ +/** + * implement a container like std::map + */ +#ifndef SJTU_MAP_HPP +#define SJTU_MAP_HPP + +// only for std::less +#include +#include +#include +#include +#ifndef NDEBUG +#include // only for debug use +#include // only for debug use +#endif +#include "exceptions.hpp" +#include "utility.hpp" + +namespace sjtu { +struct map_iterator_tag : std::bidirectional_iterator_tag {}; +template > +class map { + public: + /** + * the internal type of data. + * it should have a default constructor, a copy constructor. + * You can use sjtu::map as value_type by typedef. + */ + typedef pair value_type; + + private: + static Compare comparer; + /** + * The NIL Node is recorded as a nullptr pointer. + */ + struct RedBlackTreeNodeType { + value_type val; + RedBlackTreeNodeType *left, *right, *parent; + enum RedBlackTreeColorType { RED, BLACK } color; + RedBlackTreeNodeType() : left(nullptr), right(nullptr), parent(nullptr), color(RED) {} + RedBlackTreeNodeType(const value_type &val, RedBlackTreeNodeType *left, RedBlackTreeNodeType *right, + RedBlackTreeNodeType *parent, RedBlackTreeColorType color) + : val(val), left(left), right(right), parent(parent), color(color) {} + inline RedBlackTreeNodeType *GetGrandParent() const noexcept { + if (parent == nullptr) +#if __cplusplus >= 202002L + [[unlikely]] +#endif + return nullptr; + return parent->parent; + } + inline RedBlackTreeNodeType *GetUncle() const noexcept { + RedBlackTreeNodeType *grand_parent = GetGrandParent(); + if (grand_parent == nullptr) +#if __cplusplus >= 202002L + [[unlikely]] +#endif + return nullptr; + if (parent == grand_parent->left) + return grand_parent->right; + else + return grand_parent->left; + } + inline RedBlackTreeNodeType *GetSibling() const noexcept { + if (parent == nullptr) +#if __cplusplus >= 202002L + [[unlikely]] +#endif + return nullptr; + if (this == parent->left) + return parent->right; + else + return parent->left; + } + inline RedBlackTreeNodeType *&GetSelfPath(RedBlackTreeNodeType *&tree_root) noexcept { + if (parent == nullptr) return tree_root; + if (this == parent->left) + return parent->left; + else + return parent->right; + } + inline void SetChildrensParent() noexcept { + if (left != nullptr) left->parent = this; + if (right != nullptr) right->parent = this; + } + inline void RotateLeft(RedBlackTreeNodeType *&tree_root) noexcept { + assert(this->right != nullptr); + RedBlackTreeNodeType *parent_backup = parent; + RedBlackTreeNodeType *&path = this->GetSelfPath(tree_root); + RedBlackTreeNodeType *replacement = this->right; + this->right = replacement->left; + replacement->left = this; + this->SetChildrensParent(); + replacement->SetChildrensParent(); + path = replacement; + replacement->parent = parent_backup; + } + inline void RotateRight(RedBlackTreeNodeType *&tree_root) noexcept { + assert(this->left != nullptr); + RedBlackTreeNodeType *parent_backup = parent; + RedBlackTreeNodeType *&path = this->GetSelfPath(tree_root); + RedBlackTreeNodeType *replacement = this->left; + this->left = replacement->right; + replacement->right = this; + this->SetChildrensParent(); + replacement->SetChildrensParent(); + path = replacement; + replacement->parent = parent_backup; + } + void InsertFixUp(RedBlackTreeNodeType *&tree_root) { + if (parent == nullptr) { + // Case 1 + color = RedBlackTreeColorType::BLACK; + return; + } + if (parent->color == RedBlackTreeColorType::BLACK) return; + if (parent->parent == nullptr) { + // Case 2 & 3 + parent->color = RedBlackTreeColorType::BLACK; + return; + } + RedBlackTreeNodeType *uncle = GetUncle(); + RedBlackTreeNodeType *grand_parent = GetGrandParent(); + if (uncle != nullptr && uncle->color == RedBlackTreeColorType::RED) { + // Case 4 + parent->color = RedBlackTreeColorType::BLACK; + uncle->color = RedBlackTreeColorType::BLACK; + grand_parent->color = RedBlackTreeColorType::RED; + grand_parent->InsertFixUp(tree_root); + return; + } + if (grand_parent->left == parent) { + if (parent->right == this) { + RedBlackTreeNodeType *old_parent = parent; + parent->RotateLeft(tree_root); + assert(old_parent->parent == this); + old_parent->InsertFixUp(tree_root); + return; + } + grand_parent->RotateRight(tree_root); + assert(grand_parent->parent == parent); + parent->color = RedBlackTreeColorType::BLACK; + grand_parent->color = RedBlackTreeColorType::RED; + } else { + if (parent->left == this) { + RedBlackTreeNodeType *old_parent = parent; + parent->RotateRight(tree_root); + assert(old_parent->parent == this); + old_parent->InsertFixUp(tree_root); + return; + } + grand_parent->RotateLeft(tree_root); + assert(grand_parent->parent == parent); + parent->color = RedBlackTreeColorType::BLACK; + grand_parent->color = RedBlackTreeColorType::RED; + } + } + /** + * @brief Insert a new node into the tree. + * + * @details This function will insert a new node into the tree. If insert successfully, it will return true. + * + * @param tree_root The root of the tree. + * @param val The value to be inserted. + * @param allow_replacement Whether to allow replacement if the key already exists. + * + * @return Whether the insertion is successful and where the insertion is. + * + * @note Note that tree_root is a reference to the root of the tree. This function will modify the tree_root if + * necessary. + */ + std::pair Insert(RedBlackTreeNodeType *&tree_root, const value_type &val, + bool allow_replacement) { + if (comparer(val.first, this->val.first)) { + if (left == nullptr) { + left = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED); + left->parent = this; + RedBlackTreeNodeType *addr = left; + left->InsertFixUp(tree_root); + return std::pair(addr, true); + } else { + return left->Insert(tree_root, val, allow_replacement); + } + } else if (comparer(this->val.first, val.first)) { + if (right == nullptr) { + right = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED); + right->parent = this; + RedBlackTreeNodeType *addr = right; + right->InsertFixUp(tree_root); + return std::pair(addr, true); + } else { + return right->Insert(tree_root, val, allow_replacement); + } + } else { + if (allow_replacement) { + this->val.second = val.second; + return std::pair(this, false); + } + } + return std::pair(this, false); + } + /** + * @brief The definition of ReleaseAll. + * + * @details This fuction will be called when the whole map is destructed. It will release all the memory allocated. + * + * @note Note that the node itself must be released outside this function. + */ + void ReleaseAll() { + if (left) left->ReleaseAll(); + if (right) right->ReleaseAll(); + delete left; + delete right; + } + RedBlackTreeNodeType *Find(const decltype(val.first) &key) { + if (comparer(key, val.first)) { + if (left == nullptr) return nullptr; + return left->Find(key); + } else if (comparer(val.first, key)) { + if (right == nullptr) return nullptr; + return right->Find(key); + } else { + return this; + } + } + /** + * @brief Swap the node with its successor. + * + * @details This function will swap the node with its successor. + * + * @note The color is not swapped. + */ + inline static void SwapNodeWithItsSuccessor(RedBlackTreeNodeType *node, RedBlackTreeNodeType *successor, + RedBlackTreeNodeType *&tree_root) { + RedBlackTreeNodeType *left_of_node = node->left; + RedBlackTreeNodeType *right_of_node = node->right; + RedBlackTreeNodeType *parent_of_node = node->parent; + RedBlackTreeColorType color_of_node = node->color; + RedBlackTreeNodeType *&path_of_node = node->GetSelfPath(tree_root); + RedBlackTreeNodeType *left_of_successor = successor->left; + RedBlackTreeNodeType *right_of_successor = successor->right; + RedBlackTreeNodeType *parent_of_successor = successor->parent; + RedBlackTreeColorType color_of_successor = successor->color; + RedBlackTreeNodeType *&path_of_successor = successor->GetSelfPath(tree_root); + node->color = color_of_successor; + successor->color = color_of_node; + if (parent_of_successor == node) { + successor->left = left_of_node; + successor->right = node; + successor->SetChildrensParent(); + successor->parent = parent_of_node; + path_of_node = successor; + node->left = left_of_successor; + node->right = right_of_successor; + node->SetChildrensParent(); + } else { + successor->left = left_of_node; + successor->right = right_of_node; + successor->SetChildrensParent(); + successor->parent = parent_of_node; + path_of_node = successor; + node->left = left_of_successor; + node->right = right_of_successor; + node->SetChildrensParent(); + node->parent = parent_of_successor; + path_of_successor = node; + } + } + void DeleteFixUp(RedBlackTreeNodeType *&tree_root) { + assert(this->color == RedBlackTreeColorType::BLACK); + if (this == tree_root) return; + RedBlackTreeNodeType *sibling = GetSibling(); + assert(sibling != nullptr); + if (sibling->color == RedBlackTreeColorType::RED) { + // Case 1 + parent->color = RedBlackTreeColorType::RED; + sibling->color = RedBlackTreeColorType::BLACK; + if (this == parent->left) { + parent->RotateLeft(tree_root); + } else { + parent->RotateRight(tree_root); + } + this->DeleteFixUp(tree_root); + return; + } + RedBlackTreeNodeType *close_nephew = nullptr; + RedBlackTreeNodeType *distant_nephew = nullptr; + if (this == parent->left) { + close_nephew = sibling->left; + distant_nephew = sibling->right; + } else { + close_nephew = sibling->right; + distant_nephew = sibling->left; + } + if (sibling->color == RedBlackTreeColorType::BLACK && this->parent->color == RedBlackTreeColorType::RED && + (close_nephew == nullptr || close_nephew != nullptr && close_nephew->color == RedBlackTreeColorType::BLACK) && + (distant_nephew == nullptr || + distant_nephew != nullptr && distant_nephew->color == RedBlackTreeColorType::BLACK)) { + // Case 2 + sibling->color = RedBlackTreeColorType::RED; + this->parent->color = RedBlackTreeColorType::BLACK; + return; + } + if (sibling->color == RedBlackTreeColorType::BLACK && this->parent->color == RedBlackTreeColorType::BLACK && + (close_nephew == nullptr || close_nephew != nullptr && close_nephew->color == RedBlackTreeColorType::BLACK) && + (distant_nephew == nullptr || + distant_nephew != nullptr && distant_nephew->color == RedBlackTreeColorType::BLACK)) { + // Case 3 + sibling->color = RedBlackTreeColorType::RED; + this->parent->DeleteFixUp(tree_root); + return; + } + if (close_nephew != nullptr && close_nephew->color == RedBlackTreeColorType::RED) { + // Case 4 + if (this == parent->left) { + sibling->color = RedBlackTreeColorType::RED; + close_nephew->color = RedBlackTreeColorType::BLACK; + sibling->RotateRight(tree_root); + } else { + sibling->color = RedBlackTreeColorType::RED; + close_nephew->color = RedBlackTreeColorType::BLACK; + sibling->RotateLeft(tree_root); + } + this->DeleteFixUp(tree_root); + return; + } + assert(distant_nephew != nullptr && distant_nephew->color == RedBlackTreeColorType::RED); + // Then it must be Case 5 + if (this == parent->left) { + std::swap(sibling->color, parent->color); + distant_nephew->color = RedBlackTreeColorType::BLACK; + parent->RotateLeft(tree_root); + } else { + std::swap(sibling->color, parent->color); + distant_nephew->color = RedBlackTreeColorType::BLACK; + parent->RotateRight(tree_root); + } + } + static void DeleteNode(RedBlackTreeNodeType *pos, RedBlackTreeNodeType *&tree_root) { + if (pos->parent == nullptr && pos->left == nullptr && pos->right == nullptr) { + // Case 0: The only node in the tree. + delete pos; + tree_root = nullptr; + return; + } + if (pos->left != nullptr && pos->right != nullptr) { + // Case 1: The node has two children. Then we swap the node with its successor and just delete the successor. + RedBlackTreeNodeType *successor = pos->right; + while (successor->left != nullptr) successor = successor->left; + SwapNodeWithItsSuccessor(pos, successor, tree_root); + DeleteNode(pos, tree_root); + return; + } + if (pos->left == nullptr && pos->right == nullptr) { + // Case 2 + if (pos->color == RedBlackTreeColorType::BLACK) { + pos->DeleteFixUp(tree_root); + } + pos->GetSelfPath(tree_root) = nullptr; + delete pos; + return; + } + // Case 3 + RedBlackTreeNodeType *replacement = (pos->left != nullptr ? pos->left : pos->right); + assert(replacement != nullptr); + assert(replacement->color == RedBlackTreeColorType::RED); + pos->GetSelfPath(tree_root) = replacement; + replacement->parent = pos->parent; + replacement->color = RedBlackTreeColorType::BLACK; + delete pos; + } + static void CopyFrom(RedBlackTreeNodeType *&target, const RedBlackTreeNodeType *source) { + if (source == nullptr) return; + target = new RedBlackTreeNodeType(source->val, nullptr, nullptr, nullptr, source->color); + CopyFrom(target->left, source->left); + CopyFrom(target->right, source->right); + target->SetChildrensParent(); + } + }; + size_t node_count; + RedBlackTreeNodeType *tree_root; + + public: + /** + * see BidirectionalIterator at CppReference for help. + * + * if there is anything wrong throw invalid_iterator. + * like it = map.begin(); --it; + * or it = map.end(); ++end(); + */ + class const_iterator; + class iterator; + friend iterator; + friend const_iterator; + class iterator { + private: + RedBlackTreeNodeType *raw_pointer; // when iterator points to end(), raw_pointer=nullptr + map *domain; + + public: + // Add some type traits + typedef sjtu::map_iterator_tag iterator_category; + typedef pair value_type; + typedef std::ptrdiff_t difference_type; + typedef value_type *pointer; + typedef value_type &reference; + + friend const_iterator; + friend map; + iterator() : raw_pointer(nullptr), domain(nullptr) {} + iterator(const iterator &other) : raw_pointer(other.raw_pointer), domain(other.domain) {} + iterator(RedBlackTreeNodeType *raw_pointer, map *domain) : raw_pointer(raw_pointer), domain(domain) {} + iterator &operator++() { + if (raw_pointer == nullptr) throw invalid_iterator(); + if (raw_pointer->right != nullptr) { + raw_pointer = raw_pointer->right; + while (raw_pointer->left != nullptr) raw_pointer = raw_pointer->left; + } else { + RedBlackTreeNodeType *backup = raw_pointer; + while (raw_pointer->parent != nullptr && raw_pointer->parent->right == raw_pointer) + raw_pointer = raw_pointer->parent; + if (raw_pointer->parent == nullptr) { + raw_pointer = nullptr; + return *this; + } + raw_pointer = raw_pointer->parent; + } + return *this; + } + iterator operator++(int) { + iterator tmp = *this; + ++*this; + return tmp; + } + iterator &operator--() { + if (raw_pointer == nullptr) { + if (domain == nullptr) throw invalid_iterator(); + if (domain->tree_root == nullptr) throw invalid_iterator(); + raw_pointer = domain->tree_root; + while (raw_pointer->right != nullptr) raw_pointer = raw_pointer->right; + return *this; + } + if (raw_pointer->left != nullptr) { + raw_pointer = raw_pointer->left; + while (raw_pointer->right != nullptr) raw_pointer = raw_pointer->right; + } else { + RedBlackTreeNodeType *backup = raw_pointer; + while (raw_pointer->parent != nullptr && raw_pointer->parent->left == raw_pointer) + raw_pointer = raw_pointer->parent; + if (raw_pointer->parent == nullptr) { + throw invalid_iterator(); + raw_pointer = nullptr; + return *this; + } + raw_pointer = raw_pointer->parent; + } + return *this; + } + iterator operator--(int) { + iterator tmp = *this; + --*this; + return tmp; + } + /** + * a operator to check whether two iterators are same (pointing to the same memory). + */ + value_type &operator*() const { + if (raw_pointer == nullptr) throw invalid_iterator(); + return raw_pointer->val; + } + bool operator==(const iterator &rhs) const { return domain == rhs.domain && raw_pointer == rhs.raw_pointer; } + bool operator==(const const_iterator &rhs) const { return domain == rhs.domain && raw_pointer == rhs.raw_pointer; } + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { return domain != rhs.domain || raw_pointer != rhs.raw_pointer; } + bool operator!=(const const_iterator &rhs) const { return domain != rhs.domain || raw_pointer != rhs.raw_pointer; } + + /** + * for the support of it->first. + * See + * + * for help. + */ + value_type *operator->() const noexcept { return &raw_pointer->val; } + }; + class const_iterator { + private: + RedBlackTreeNodeType *raw_pointer; // when iterator points to end(), raw_pointer=nullptr + const map *domain; + + public: + // Add some type traits + typedef sjtu::map_iterator_tag iterator_category; + typedef pair value_type; + typedef std::ptrdiff_t difference_type; + typedef value_type *pointer; + typedef value_type &reference; + + friend iterator; + friend map; + const_iterator() : raw_pointer(nullptr), domain(nullptr) {} + const_iterator(const const_iterator &other) : raw_pointer(other.raw_pointer), domain(other.domain) {} + const_iterator(const iterator &other) : raw_pointer(other.raw_pointer), domain(other.domain) {} + const_iterator(RedBlackTreeNodeType *raw_pointer, const map *domain) : raw_pointer(raw_pointer), domain(domain) {} + const_iterator &operator++() { + if (raw_pointer == nullptr) throw invalid_iterator(); + if (raw_pointer->right != nullptr) { + raw_pointer = raw_pointer->right; + while (raw_pointer->left != nullptr) raw_pointer = raw_pointer->left; + } else { + RedBlackTreeNodeType *backup = raw_pointer; + while (raw_pointer->parent != nullptr && raw_pointer->parent->right == raw_pointer) + raw_pointer = raw_pointer->parent; + if (raw_pointer->parent == nullptr) { + raw_pointer = nullptr; + return *this; + } + raw_pointer = raw_pointer->parent; + } + return *this; + } + const_iterator operator++(int) { + const_iterator tmp = *this; + ++*this; + return tmp; + } + const_iterator &operator--() { + if (raw_pointer == nullptr) { + if (domain == nullptr) throw invalid_iterator(); + if (domain->tree_root == nullptr) throw invalid_iterator(); + raw_pointer = domain->tree_root; + while (raw_pointer->right != nullptr) raw_pointer = raw_pointer->right; + return *this; + } + if (raw_pointer->left != nullptr) { + raw_pointer = raw_pointer->left; + while (raw_pointer->right != nullptr) raw_pointer = raw_pointer->right; + } else { + RedBlackTreeNodeType *backup = raw_pointer; + while (raw_pointer->parent != nullptr && raw_pointer->parent->left == raw_pointer) + raw_pointer = raw_pointer->parent; + if (raw_pointer->parent == nullptr) { + throw invalid_iterator(); + raw_pointer = nullptr; + return *this; + } + raw_pointer = raw_pointer->parent; + } + return *this; + } + const_iterator operator--(int) { + const_iterator tmp = *this; + --*this; + return tmp; + } + /** + * a operator to check whether two iterators are same (pointing to the same memory). + */ + const value_type &operator*() const { + if (raw_pointer == nullptr) throw invalid_iterator(); + return raw_pointer->val; + } + bool operator==(const iterator &rhs) const { return domain == rhs.domain && raw_pointer == rhs.raw_pointer; } + bool operator==(const const_iterator &rhs) const { return domain == rhs.domain && raw_pointer == rhs.raw_pointer; } + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { return domain != rhs.domain || raw_pointer != rhs.raw_pointer; } + bool operator!=(const const_iterator &rhs) const { return domain != rhs.domain || raw_pointer != rhs.raw_pointer; } + value_type *operator->() const noexcept { return &raw_pointer->val; } + }; + map() : node_count(0), tree_root(nullptr) {} + map(const map &other) { + node_count = other.node_count; + tree_root = nullptr; + RedBlackTreeNodeType::CopyFrom(tree_root, other.tree_root); + } + map(map &&other) { + node_count = other.node_count; + tree_root = other.tree_root; + other.node_count = 0; + other.tree_root = nullptr; + } + /** + * assignment operator + */ + map &operator=(const map &other) { + if (this == &other) return *this; + if (tree_root) tree_root->ReleaseAll(); + delete tree_root; + node_count = other.node_count; + RedBlackTreeNodeType::CopyFrom(tree_root, other.tree_root); + return *this; + } + map &operator=(map &&other) { + if (this == &other) return *this; + if (tree_root) tree_root->ReleaseAll(); + delete tree_root; + node_count = other.node_count; + tree_root = other.tree_root; + other.node_count = 0; + other.tree_root = nullptr; + return *this; + } + ~map() { + if (tree_root) tree_root->ReleaseAll(); + delete tree_root; + } + /** + * access specified element with bounds checking + * Returns a reference to the mapped value of the element with key equivalent to key. + * If no such element exists, an exception of type `index_out_of_bound' + */ + T &at(const Key &key) { + if (tree_root == nullptr) throw index_out_of_bound(); + RedBlackTreeNodeType *result = tree_root->Find(key); + if (result == nullptr) throw index_out_of_bound(); + return result->val.second; + } + const T &at(const Key &key) const { + if (tree_root == nullptr) throw index_out_of_bound(); + RedBlackTreeNodeType *result = tree_root->Find(key); + if (result == nullptr) throw index_out_of_bound(); + return result->val.second; + } + /** + * access specified element + * Returns a reference to the value that is mapped to a key equivalent to key, + * performing an insertion if such key does not already exist. + */ + T &operator[](const Key &key) { + if (node_count == 0) { + tree_root = + new RedBlackTreeNodeType(value_type(key, T()), nullptr, nullptr, nullptr, RedBlackTreeNodeType::BLACK); + ++node_count; + return tree_root->val.second; + } + auto result = tree_root->Insert(tree_root, value_type(key, T()), false); + if (result.second) ++node_count; + return result.first->val.second; + } + /** + * behave like at() throw index_out_of_bound if such key does not exist. + */ + const T &operator[](const Key &key) const { return at(key); } + /** + * return a iterator to the beginning + */ + iterator begin() { + if (tree_root == nullptr) return iterator(nullptr, this); + RedBlackTreeNodeType *tmp = tree_root; + while (tmp->left != nullptr) tmp = tmp->left; + return iterator(tmp, this); + } + const_iterator begin() const { return cbegin(); } + const_iterator cbegin() const { + if (tree_root == nullptr) return const_iterator(nullptr, this); + RedBlackTreeNodeType *tmp = tree_root; + while (tmp->left != nullptr) tmp = tmp->left; + return const_iterator(tmp, this); + } + /** + * return a iterator to the end + * in fact, it returns past-the-end. + */ + iterator end() { return iterator(nullptr, this); } + const_iterator end() const { return cend(); } + const_iterator cend() const { return const_iterator(nullptr, this); } + /** + * checks whether the container is empty + * return true if empty, otherwise false. + */ + bool empty() const { return node_count == 0; } + /** + * returns the number of elements. + */ + size_t size() const { return node_count; } + /** + * clears the contents + */ + void clear() { + if (tree_root) tree_root->ReleaseAll(); + delete tree_root; + tree_root = nullptr; + node_count = 0; + } + /** + * insert an element. + * return a pair, the first of the pair is + * the iterator to the new element (or the element that prevented the insertion), + * the second one is true if insert successfully, or false. + */ + pair insert(const value_type &value) { + if (tree_root == nullptr) { + tree_root = new RedBlackTreeNodeType(value, nullptr, nullptr, nullptr, RedBlackTreeNodeType::BLACK); + node_count = 1; + return pair(iterator(tree_root, this), true); + } + auto result = tree_root->Insert(tree_root, value, false); + if (result.second) ++node_count; + return pair(iterator(result.first, this), result.second); + } + /** + * erase the element at pos. + * + * throw if pos pointed to a bad element (pos == this->end() || pos points an element out of this) + */ + void erase(iterator pos) { + if (pos.domain != this || pos.raw_pointer == nullptr) throw invalid_iterator(); + RedBlackTreeNodeType::DeleteNode(pos.raw_pointer, tree_root); + --node_count; + } + /** + * Returns the number of elements with key + * that compares equivalent to the specified argument, + * which is either 1 or 0 + * since this container does not allow duplicates. + * The default method of check the equivalence is !(a < b || b > a) + */ + size_t count(const Key &key) const { + if (tree_root == nullptr) return 0; + return tree_root->Find(key) == nullptr ? 0 : 1; + } + /** + * Finds an element with key equivalent to key. + * key value of the element to search for. + * Iterator to an element with key equivalent to key. + * If no such element is found, past-the-end (see end()) iterator is returned. + */ + iterator find(const Key &key) { + if (tree_root == nullptr) return end(); + return iterator(tree_root->Find(key), this); + } + const_iterator find(const Key &key) const { + if (tree_root == nullptr) return cend(); + return const_iterator(tree_root->Find(key), this); + } +#ifndef NDEBUG + bool RedBlackTreeStructureCheck() { + if (tree_root == nullptr) return node_count == 0; + if (node_count == 0) return false; + std::queue Q; + std::vector NIL_leafs; + size_t actual_node_count = 0; + Q.push(tree_root); + while (!Q.empty()) { + RedBlackTreeNodeType *current = Q.front(); + Q.pop(); + if (current->color == RedBlackTreeNodeType::RED) { + if (current->left != nullptr && current->left->color == RedBlackTreeNodeType::RED) return false; + if (current->right != nullptr && current->right->color == RedBlackTreeNodeType::RED) return false; + } + if (current->left != nullptr) + Q.push(current->left); + else + NIL_leafs.push_back(current); + if (current->right != nullptr) + Q.push(current->right); + else + NIL_leafs.push_back(current); + ++actual_node_count; + } + if (actual_node_count != node_count) return false; + if (tree_root->color != RedBlackTreeNodeType::BLACK) return false; + if (tree_root->parent != nullptr) return false; + assert(NIL_leafs.size() >= 2); + size_t correct_black_nodes = 1; + RedBlackTreeNodeType *ptr = NIL_leafs[0]; + while (ptr) { + if (ptr->color == RedBlackTreeNodeType::BLACK) ++correct_black_nodes; + ptr = ptr->parent; + } + for (auto ptr : NIL_leafs) { + size_t black_nodes = 1; + while (ptr) { + if (ptr->color == RedBlackTreeNodeType::BLACK) ++black_nodes; + ptr = ptr->parent; + } + if (black_nodes != correct_black_nodes) return false; + } + // Now check whether it is a binary search tree. Use a lambda expression to get inorder tree walk. + std::vector key_array; + std::function inorder_walk = [&](RedBlackTreeNodeType *node) { + if (node->left != nullptr) inorder_walk(node->left); + key_array.push_back(node->val.first); + if (node->right != nullptr) inorder_walk(node->right); + }; + inorder_walk(tree_root); + for (size_t i = 1; i < key_array.size(); ++i) { + if (!comparer(key_array[i - 1], key_array[i])) return false; + } + return true; + } +#endif +}; +// Define the static member comparer. +template +Compare map::comparer = Compare(); + +} // namespace sjtu + +#endif \ No newline at end of file diff --git a/stlite/priority_queue.hpp b/stlite/priority_queue.hpp new file mode 100644 index 0000000..35ce711 --- /dev/null +++ b/stlite/priority_queue.hpp @@ -0,0 +1,129 @@ +#ifndef SJTU_PRIORITY_QUEUE_HPP +#define SJTU_PRIORITY_QUEUE_HPP + +#include +#include +#include "exceptions.hpp" + +namespace sjtu { + +/** + * a container like std::priority_queue which is a heap internal. + * Use Skew heap to implement this priority_queue. + */ +template > +class priority_queue { + Compare comp; + struct Node { + T value; + Node *left, *right; + Node(const T &v) : value(v), left(nullptr), right(nullptr) {} + } * root; + size_t node_count; + void FreeAll(Node *node) { + if (node == nullptr) return; + FreeAll(node->left); + FreeAll(node->right); + delete node; + } + void CopyAll(Node *&dest, Node *src) { + if (src == nullptr) return; + dest = new Node(src->value); + CopyAll(dest->left, src->left); + CopyAll(dest->right, src->right); + } + + Node *SkewMerge(Node *a, Node *b) { + if (a == nullptr) return b; + if (b == nullptr) return a; + if (comp(a->value, b->value)) std::swap(a, b); + a->right = SkewMerge(a->right, b); + std::swap(a->left, a->right); + return a; + } + + public: + priority_queue() : root(nullptr), node_count(0) {} + priority_queue(const priority_queue &other) { + root = nullptr; + CopyAll(root, other.root); + node_count = other.node_count; + } + /** + * TODO deconstructor + */ + ~priority_queue() { + FreeAll(root); + root = nullptr; + } + /** + * TODO Assignment operator + */ + priority_queue &operator=(const priority_queue &other) { + if (this == &other) return *this; + FreeAll(root); + root = nullptr; + CopyAll(root, other.root); + node_count = other.node_count; + return *this; + } + /** + * get the top of the queue. + * @return a reference of the top element. + * throw container_is_empty if empty() returns true; + */ + const T &top() const { + if (empty()) throw container_is_empty(); + return root->value; + } + /** + * TODO + * push new element to the priority queue. + */ + void push(const T &e) { + Node *new_node = new Node(e); + try { + root = SkewMerge(root, new_node); + } catch (...) { + delete new_node; + throw; + } + ++node_count; + } + /** + * TODO + * delete the top element. + * throw container_is_empty if empty() returns true; + */ + void pop() { + if (empty()) throw container_is_empty(); + Node *old_root = root; + root = SkewMerge(root->left, root->right); + delete old_root; + --node_count; + } + /** + * return the number of the elements. + */ + size_t size() const { return node_count; } + /** + * check if the container has at least an element. + * @return true if it is empty, false if it has at least an element. + */ + bool empty() const { return node_count == 0; } + /** + * merge two priority_queues with at most O(logn) complexity. + * clear the other priority_queue. + */ + void merge(priority_queue &other) { + if (other.root == root) return; + root = SkewMerge(root, other.root); + node_count += other.node_count; + other.root = nullptr; + other.node_count = 0; + } +}; + +} // namespace sjtu + +#endif diff --git a/stlite/utility.hpp b/stlite/utility.hpp new file mode 100644 index 0000000..4d13ee5 --- /dev/null +++ b/stlite/utility.hpp @@ -0,0 +1,27 @@ +#ifndef SJTU_UTILITY_HPP +#define SJTU_UTILITY_HPP + +#include + +namespace sjtu { + +template +class pair { +public: + T1 first; + T2 second; + constexpr pair() : first(), second() {} + pair(const pair &other) = default; + pair(pair &&other) = default; + pair(const T1 &x, const T2 &y) : first(x), second(y) {} + template + pair(U1 &&x, U2 &&y) : first(x), second(y) {} + template + pair(const pair &other) : first(other.first), second(other.second) {} + template + pair(pair &&other) : first(other.first), second(other.second) {} +}; + +} + +#endif diff --git a/stlite/vector.hpp b/stlite/vector.hpp new file mode 100644 index 0000000..be66fbf --- /dev/null +++ b/stlite/vector.hpp @@ -0,0 +1,543 @@ +#ifndef SJTU_VECTOR_HPP +#define SJTU_VECTOR_HPP + +#include "exceptions.hpp" + +#include +#include +#include +#include +#include + +namespace sjtu { +/** + * a data container like std::vector + * store data in a successive memory and support random access. + */ +template +class vector { + static std::allocator alloc; + size_t allocated_length; + size_t current_length; + T *raw_beg, *raw_end; + + public: + /** + * you can see RandomAccessIterator at CppReference for help. + */ + class const_iterator; + class iterator { + // The following code is written for the C++ type_traits library. + // Type traits is a C++ feature for describing certain properties of a type. + // For instance, for an iterator, iterator::value_type is the type that the + // iterator points to. + // STL algorithms and containers may use these type_traits (e.g. the following + // typedef) to work properly. In particular, without the following code, + // @code{std::sort(iter, iter1);} would not compile. + // See these websites for more information: + // https://en.cppreference.com/w/cpp/header/type_traits + // About value_type: https://blog.csdn.net/u014299153/article/details/72419713 + // About iterator_category: https://en.cppreference.com/w/cpp/iterator + friend class vector; + + public: + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T *; + using reference = T &; + using iterator_category = std::random_access_iterator_tag; + + private: + vector *domain; + T *raw_pointer; + iterator(vector *domain, T *raw_pointer) : domain(domain), raw_pointer(raw_pointer) {} + + public: + /** + * return a new iterator which pointer n-next elements + * as well as operator- + */ + iterator operator+(const int &n) const { + iterator temp = *this; + temp.raw_pointer += n; + return temp; + } + iterator operator-(const int &n) const { + iterator temp = *this; + temp.raw_pointer -= n; + return temp; + } + // return the distance between two iterators, + // if these two iterators point to different vectors, throw invaild_iterator. + int operator-(const iterator &rhs) const { + if (domain != rhs.domain) [[unlikely]] + throw invalid_iterator(); + return raw_pointer - rhs.raw_pointer; + } + iterator &operator+=(const int &n) { + raw_pointer += n; + return *this; + } + iterator &operator-=(const int &n) { + raw_pointer -= n; + return *this; + } + /** + * TODO iter++ + */ + iterator operator++(int) { + iterator temp = *this; + raw_pointer++; + return temp; + } + /** + * TODO ++iter + */ + iterator &operator++() { + raw_pointer++; + return *this; + } + /** + * TODO iter-- + */ + iterator operator--(int) { + iterator temp = *this; + raw_pointer--; + return temp; + } + /** + * TODO --iter + */ + iterator &operator--() { + raw_pointer--; + return *this; + } + /** + * TODO *it + */ + T &operator*() const { return *raw_pointer; } + /** + * a operator to check whether two iterators are same (pointing to the same memory address). + */ + bool operator==(const iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + bool operator==(const const_iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + bool operator!=(const const_iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + }; + /** + * TODO + * has same function as iterator, just for a const object. + */ + class const_iterator { + friend class vector; + + public: + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T *; + using reference = T &; + using iterator_category = std::random_access_iterator_tag; + + private: + const vector *domain; + const T *raw_pointer; + inline const_iterator(const vector *domain, const T *raw_pointer) : domain(domain), raw_pointer(raw_pointer) {} + + public: + /** + * return a new iterator which pointer n-next elements + * as well as operator- + */ + const_iterator operator+(const int &n) const { + const_iterator temp = *this; + temp.raw_pointer += n; + return temp; + } + const_iterator operator-(const int &n) const { + const_iterator temp = *this; + temp.raw_pointer -= n; + return temp; + } + // return the distance between two iterators, + // if these two iterators point to different vectors, throw invaild_iterator. + int operator-(const const_iterator &rhs) const { + if (domain != rhs.domain) [[unlikely]] + throw invalid_iterator(); + return raw_pointer - rhs.raw_pointer; + } + const_iterator &operator+=(const int &n) { + raw_pointer += n; + return *this; + } + const_iterator &operator-=(const int &n) { + raw_pointer -= n; + return *this; + } + /** + * TODO iter++ + */ + const_iterator operator++(int) { + const_iterator temp = *this; + raw_pointer++; + return temp; + } + /** + * TODO ++iter + */ + const_iterator &operator++() { + raw_pointer++; + return *this; + } + /** + * TODO iter-- + */ + const_iterator operator--(int) { + const_iterator temp = *this; + raw_pointer--; + return temp; + } + /** + * TODO --iter + */ + const_iterator &operator--() { + raw_pointer--; + return *this; + } + /** + * TODO *it + */ + const T &operator*() const { return *raw_pointer; } + /** + * a operator to check whether two iterators are same (pointing to the same memory address). + */ + bool operator==(const iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + bool operator==(const const_iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + bool operator!=(const const_iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + }; + /** + * TODO Constructs + * At least two: default constructor, copy constructor + */ + vector() { + raw_beg = alloc.allocate(1); + raw_end = raw_beg; + allocated_length = 1; + current_length = 0; + } + vector(const vector &other) { + raw_beg = alloc.allocate(other.allocated_length); + raw_end = raw_beg + other.current_length; + allocated_length = other.allocated_length; + current_length = other.current_length; + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::construct(alloc, raw_beg + i, other.raw_beg[i]); + } + } + vector(vector &&other) noexcept { + raw_beg = other.raw_beg; + raw_end = other.raw_end; + allocated_length = other.allocated_length; + current_length = other.current_length; + other.raw_beg = nullptr; + other.raw_end = nullptr; + other.allocated_length = 0; + other.current_length = 0; + } + ~vector() { + if (raw_beg != nullptr) { + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::destroy(alloc, raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + } + } + /** + * TODO Assignment operator + */ + vector &operator=(const vector &other) { + if (this == &other) return *this; + if (raw_beg != nullptr) { + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::destroy(alloc, raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + } + raw_beg = alloc.allocate(other.allocated_length); + raw_end = raw_beg + other.current_length; + allocated_length = other.allocated_length; + current_length = other.current_length; + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::construct(alloc, raw_beg + i, other.raw_beg[i]); + } + return *this; + } + vector &operator=(vector &&other) noexcept { + if (this == &other) return *this; + if (raw_beg != nullptr) { + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::destroy(alloc, raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + } + raw_beg = other.raw_beg; + raw_end = other.raw_end; + allocated_length = other.allocated_length; + current_length = other.current_length; + other.raw_beg = nullptr; + other.raw_end = nullptr; + other.allocated_length = 0; + other.current_length = 0; + return *this; + } + /** + * assigns specified element with bounds checking + * throw index_out_of_bound if pos is not in [0, size) + */ + T &at(const size_t &pos) { + if (pos < 0 || pos >= current_length) [[unlikely]] + throw index_out_of_bound(); + return raw_beg[pos]; + } + const T &at(const size_t &pos) const { + if (pos < 0 || pos >= current_length) [[unlikely]] + throw index_out_of_bound(); + return raw_beg[pos]; + } + /** + * assigns specified element with bounds checking + * throw index_out_of_bound if pos is not in [0, size) + * !!! Pay attentions + * In STL this operator does not check the boundary but I want you to do. + */ + T &operator[](const size_t &pos) { + if (pos < 0 || pos >= current_length) [[unlikely]] + throw index_out_of_bound(); + return raw_beg[pos]; + } + const T &operator[](const size_t &pos) const { + if (pos < 0 || pos >= current_length) [[unlikely]] + throw index_out_of_bound(); + return raw_beg[pos]; + } + /** + * access the first element. + * throw container_is_empty if size == 0 + */ + const T &front() const { + if (current_length == 0) [[unlikely]] + throw container_is_empty(); + return raw_beg[0]; + } + /** + * access the last element. + * throw container_is_empty if size == 0 + */ + const T &back() const { + if (current_length == 0) [[unlikely]] + throw container_is_empty(); + return raw_end[-1]; + } + /** + * returns an iterator to the beginning. + */ + iterator begin() { return iterator(this, raw_beg); } + const_iterator begin() const { return const_iterator(this, raw_beg); } + const_iterator cbegin() const { return const_iterator(this, raw_beg); } + /** + * returns an iterator to the end. + */ + iterator end() { return iterator(this, raw_end); } + const_iterator end() const { return const_iterator(this, raw_end); } + const_iterator cend() const { return const_iterator(this, raw_end); } + /** + * checks whether the container is empty + */ + bool empty() const { return current_length == 0; } + /** + * returns the number of elements + */ + size_t size() const { return current_length; } + /** + * clears the contents + */ + void clear() { + if (raw_beg != nullptr) { + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::destroy(alloc, raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + } + raw_beg = alloc.allocate(1); + raw_end = raw_beg; + allocated_length = 1; + current_length = 0; + } + /** + * inserts value before pos + * returns an iterator pointing to the inserted value. + */ + iterator insert(iterator pos, const T &value) { + if (pos.raw_pointer < raw_beg || pos.raw_pointer > raw_end) throw invalid_iterator(); + if (current_length == allocated_length) { + size_t new_allocated_length = allocated_length * 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + pos.raw_pointer = new_raw_beg + (pos.raw_pointer - raw_beg); + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::construct(alloc, new_raw_beg + i, std::move(raw_beg[i])); + std::allocator_traits::destroy(alloc, raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + for (T *i = raw_end; i != pos.raw_pointer; --i) { + std::allocator_traits::construct(alloc, i, std::move(*(i - 1))); + std::allocator_traits::destroy(alloc, i - 1); + } + std::allocator_traits::construct(alloc, pos.raw_pointer, value); + raw_end++; + current_length++; + return pos; + } + /** + * inserts value at index ind. + * after inserting, this->at(ind) == value + * returns an iterator pointing to the inserted value. + * throw index_out_of_bound if ind > size (in this situation ind can be size because after inserting the size will + * increase 1.) + */ + iterator insert(const size_t &ind, const T &value) { + if (ind < 0 || ind > current_length) throw index_out_of_bound(); + if (current_length == allocated_length) { + size_t new_allocated_length = allocated_length * 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + for (size_t i = 0; i < current_length; ++i) { + alloc.construct(new_raw_beg + i, std::move(raw_beg[i])); + alloc.destroy(raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + for (T *i = raw_end; i != raw_beg + ind; --i) { + alloc.construct(i, std::move(*(i - 1))); + alloc.destroy(i - 1); + } + alloc.construct(raw_beg + ind, value); + raw_end++; + current_length++; + return iterator(this, raw_beg + ind); + } + /** + * removes the element at pos. + * return an iterator pointing to the following element. + * If the iterator pos refers the last element, the end() iterator is returned. + */ + iterator erase(iterator pos) { + if (pos.raw_pointer < raw_beg || pos.raw_pointer >= raw_end) throw invalid_iterator(); + for (T *i = pos.raw_pointer; i != raw_end - 1; ++i) { + std::allocator_traits::construct(alloc, i, std::move(*(i + 1))); + std::allocator_traits::destroy(alloc, i + 1); + } + raw_end--; + current_length--; + if (current_length != 0 && current_length <= allocated_length / 4) { + size_t new_allocated_length = allocated_length / 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::construct(alloc, new_raw_beg + i, std::move(raw_beg[i])); + std::allocator_traits::destroy(alloc, raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + return pos; + } + /** + * removes the element with index ind. + * return an iterator pointing to the following element. + * throw index_out_of_bound if ind >= size + */ + iterator erase(const size_t &ind) { + if (ind < 0 || ind >= current_length) throw index_out_of_bound(); + for (T *i = raw_beg + ind; i != raw_end - 1; ++i) { + alloc.construct(i, std::move(*(i + 1))); + alloc.destroy(i + 1); + } + raw_end--; + current_length--; + if (current_length != 0 && current_length <= allocated_length / 4) { + size_t new_allocated_length = allocated_length / 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + for (size_t i = 0; i < current_length; ++i) { + alloc.construct(new_raw_beg + i, std::move(raw_beg[i])); + alloc.destroy(raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + return iterator(this, raw_beg + ind); + } + /** + * adds an element to the end. + */ + void push_back(const T &value) { + if (current_length == allocated_length) [[unlikely]] { + size_t new_allocated_length = allocated_length * 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::construct(alloc, new_raw_beg + i, std::move(raw_beg[i])); + std::allocator_traits::destroy(alloc, raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + std::allocator_traits::construct(alloc, raw_end, value); + raw_end++; + current_length++; + } + /** + * remove the last element from the end. + * throw container_is_empty if size() == 0 + */ + void pop_back() { + if (current_length == 0) [[unlikely]] + throw container_is_empty(); + std::allocator_traits::destroy(alloc, raw_end - 1); + raw_end--; + current_length--; + if (current_length != 0 && current_length <= allocated_length / 4) [[unlikely]] { + size_t new_allocated_length = allocated_length / 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + for (size_t i = 0; i < current_length; ++i) { + std::allocator_traits::construct(alloc, new_raw_beg + i, std::move(raw_beg[i])); + std::allocator_traits::destroy(alloc, raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + } +}; +template +std::allocator vector::alloc; + +} // namespace sjtu + +#endif