// // Created by Patrick Maschek on 19.01.2024. // #ifndef UDIFF_CONST_LIST_H_ #define UDIFF_CONST_LIST_H_ #include #include #include #include namespace cc { template class const_list_node; template class const_list; template class _const_list_iterator { public: using node_N = const_list_node; using const_node_N = const const_list_node; using node_base_type = std::conditional<_const, const_node_N, node_N>::type; using node_type = std::conditional<_const, const N, N>::type; using difference_type = std::ptrdiff_t; using value_type = node_type; using pointer = value_type*; using reference = value_type&; using iterator_category = std::bidirectional_iterator_tag; node_base_type* _node = nullptr; constexpr _const_list_iterator() noexcept = default; constexpr explicit _const_list_iterator(node_base_type *node) noexcept : _node(node) {} template = true> constexpr _const_list_iterator(const _const_list_iterator& other) noexcept : _node(other._node) {} constexpr node_type& operator*() const noexcept { return *dynamic_cast(_node); } constexpr node_type* operator->() const noexcept { return dynamic_cast(_node); } constexpr _const_list_iterator &operator++() noexcept { _node = _node->_next; return *this; } constexpr _const_list_iterator &operator--() noexcept { _node = _node->_prev; return *this; } constexpr _const_list_iterator operator++(int) noexcept { auto tmp = *this; _node = _node->_next; return tmp; } constexpr _const_list_iterator operator--(int) noexcept { auto tmp = *this; _node = _node->_prev; return tmp; } template [[nodiscard]] constexpr bool operator==(const _const_list_iterator &other) const noexcept { return _node == other._node; } private: constexpr _const_list_iterator _const_cast() const noexcept requires (_const) { return _const_list_iterator(const_cast(_node)); } friend class const_list; }; template class const_list { private: static consteval void asserts() { static_assert(std::is_base_of_v, Node>, "Can only create const_list with elements derived from const_list_node"); } public: using value_type = Node; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type *; using const_pointer = const value_type *; using iterator = _const_list_iterator; using const_iterator = _const_list_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; private: const_list_node _tail { this }; std::size_t _size = 0; public: constexpr const_list() noexcept { asserts(); }; constexpr const_list(const const_list&) = delete; constexpr const_list(const_list&& other) noexcept; constexpr const_list(std::initializer_list> init) noexcept; constexpr ~const_list(); constexpr const_list& operator=(const const_list&) = delete; constexpr const_list& operator=(const_list&& other) noexcept; constexpr const_list& operator=(std::initializer_list> init) noexcept; constexpr void assign(std::initializer_list> init) noexcept; [[nodiscard]] constexpr reference front() noexcept { return *dynamic_cast(_tail._next); } [[nodiscard]] constexpr const_reference front() const noexcept { return *dynamic_cast(_tail._next); } [[nodiscard]] constexpr reference back() noexcept { return *dynamic_cast(_tail._prev); } [[nodiscard]] constexpr const_reference back() const noexcept { return *dynamic_cast(_tail._prev); } [[nodiscard]] constexpr iterator begin() noexcept { return iterator(_tail._next); }; [[nodiscard]] constexpr const_iterator begin() const noexcept { return const_iterator(_tail._next); }; [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return const_iterator(_tail._next); }; [[nodiscard]] constexpr iterator end() noexcept { return iterator(&_tail); }; [[nodiscard]] constexpr const_iterator end() const noexcept { return const_iterator(&_tail); }; [[nodiscard]] constexpr const_iterator cend() const noexcept { return const_iterator(&_tail); }; [[nodiscard]] constexpr reverse_iterator rbegin() noexcept { return std::reverse_iterator(end()); }; [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator(end()); }; [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return std::reverse_iterator(end()); }; [[nodiscard]] constexpr reverse_iterator rend() noexcept { return std::reverse_iterator(begin()); }; [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return std::reverse_iterator(begin()); }; [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return std::reverse_iterator(begin()); }; [[nodiscard]] constexpr bool empty() const noexcept { return _size == 0; } [[nodiscard]] constexpr size_type size() const noexcept { return _size; } [[nodiscard]] constexpr size_type max_size() const noexcept { return SIZE_MAX; } void clear() noexcept; constexpr iterator insert(const_iterator pos, value_type& value) noexcept; constexpr iterator insert(const_list::const_iterator pos, std::initializer_list> values) noexcept; constexpr iterator erase(const_list::const_iterator pos) noexcept; constexpr iterator erase(const_list::const_iterator first, const_list::const_iterator last) noexcept; constexpr void push_back(value_type& value) noexcept; constexpr void pop_back() noexcept; constexpr void push_front(value_type& value) noexcept; constexpr void pop_front() noexcept; constexpr void swap(const_list& other) noexcept; constexpr void merge(const_list& other); constexpr void merge(const_list&& other); template constexpr void merge(const_list& other, Compare comp); template constexpr void merge(const_list&& other, Compare comp); constexpr void splice(const_iterator pos, const_list& other); constexpr void splice(const_iterator pos, const_list&& other); constexpr void splice(const_iterator pos, const_list& other, const_iterator it); constexpr void splice(const_iterator pos, const_list&& other, const_iterator it); constexpr void splice(const_iterator pos, const_list& other, const_iterator first, const_iterator last); constexpr void splice(const_iterator pos, const_list&& other, const_iterator first, const_iterator last); constexpr size_type remove(const value_type& value); template size_type remove_if(UnaryPredicate p); void reverse() noexcept; size_type unique(); template constexpr size_type unique(BinaryPredicate p); constexpr void sort(); template void sort(Compare comp); }; template class const_list_node { private: static consteval void asserts() { static_assert(std::is_base_of_v, D>, "Template parameter has to be a subclass of const_list_node<*self*>"); } private: const_list_node *_prev = this; const_list_node *_next = this; const_list *_owner = nullptr; void (*_delete_cb)() = nullptr; public: constexpr const_list_node() noexcept { asserts(); } constexpr const_list_node(const const_list_node& other) noexcept; constexpr const_list_node(const_list_node&& other) noexcept; virtual constexpr ~const_list_node(); constexpr const_list_node& operator=(const const_list_node& other) noexcept; constexpr const_list_node& operator=(const_list_node&& other) noexcept; protected: constexpr void on_delete(void (*cb)()) noexcept; private: constexpr explicit const_list_node(const_list *owner) : _owner(owner) { asserts(); }; constexpr void push_before(const_list_node *node) noexcept; constexpr void unlink() noexcept; template friend class _const_list_iterator; friend class const_list; }; template constexpr bool operator==(const const_list& lhs, const const_list& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template constexpr auto operator<=>(const const_list& lhs, const const_list& rhs) { return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template constexpr void swap(const const_list& lhs, const const_list& rhs) { lhs.swap(rhs); } template constexpr const_list::size_type erase(const_list& c, const N& value) { c.remove(value); } template constexpr const_list::size_type erase(const_list& c, Pred pred) { c.remove_if(pred); } template constexpr const_list::const_list(const_list &&other) noexcept : _size(other._size) { asserts(); for (auto& node : other) { node._owner = this; } _tail = std::move(other._tail); } template constexpr const_list::const_list(std::initializer_list> init) noexcept : _tail(), _size(init.size()) { asserts(); assign(init); } template constexpr const_list::~const_list() { clear(); } template constexpr const_list &const_list::operator=(const_list &&other) noexcept { for (auto& node : other) { node._owner = this; } _tail = std::move(other._tail); _size = other._size; return *this; } template constexpr const_list &const_list::operator=(std::initializer_list> init) noexcept { assign(init); return *this; } template constexpr void const_list::assign(std::initializer_list> init) noexcept { clear(); insert(begin(), init); } template void const_list::clear() noexcept { for (auto node = _tail._next; _tail._next != std::addressof(_tail); ) { auto next = node->_next; node->unlink(); node = next; } _size = 0; } template constexpr const_list::iterator const_list::insert(const_iterator pos, value_type &value) noexcept { auto it = pos._const_cast(); it->push_before(value); ++_size; return --it; } template constexpr const_list::iterator const_list::insert(const_list::const_iterator pos, std::initializer_list> values) noexcept { auto it = pos._const_cast(); for (auto& value : values) { // it's operator-> may fail as it could be the tail node it._node->push_before(std::addressof(value.get())); } _size += values.size(); return it; } template constexpr const_list::iterator const_list::erase(const_list::const_iterator pos) noexcept { auto it = pos._const_cast(); auto next = std::next(it); it->unlink(); --_size; return next; } template constexpr const_list::iterator const_list::erase(const_list::const_iterator first, const_list::const_iterator last) noexcept { auto nc_last = last._const_cast(); if (first == last) { return nc_last; } auto next = (last == end() ? end() : std::next(nc_last)); for (auto it = first._const_cast()._node; it != next._node; ) { auto n = it->_next; it->unlink(); --_size; it = n; } return next; } template constexpr void const_list::push_back(value_type &value) noexcept { _tail.push_before(std::addressof(value)); ++_size; } template constexpr void const_list::pop_back() noexcept { _tail._prev->unlink(); --_size; } template constexpr void const_list::push_front(value_type &value) noexcept { _tail._next->push_before(std::addressof(value)); ++_size; } template constexpr void const_list::pop_front() noexcept { _tail._next->unlink(); --_size; } template constexpr void const_list::swap(const_list &other) noexcept { auto tmp = _tail; _tail = other._tail; other._tail = tmp; } template constexpr void const_list::merge(const_list &other) { merge(std::forward(other), [](const value_type& v1, const value_type& v2) -> bool { return v1 < v2; }); } template constexpr void const_list::merge(const_list &&other) { merge(std::forward(other), [](const value_type& v1, const value_type& v2) -> bool { return v1 < v2; }); } template template constexpr void const_list::merge(const_list &other, Compare comp) { if (std::addressof(other) == this) return; auto first1 = begin(); auto last1 = end(); auto first2 = other.begin(); auto last2 = other.end(); while (first1 != last1 && first2 != last2) { if (comp(*first2, *first1)) { auto next = std::next(first2); first1._node->push_before(first2._node); ++_size; first2 = next; } else { ++first1; } } while (first2 != last2) { auto next = std::next(first2); _tail.push_before(first2._node); ++_size; first2 = next; } other._size = 0; } template template constexpr void const_list::merge(const_list &&other, Compare comp) { if (std::addressof(other) == this) return; auto first1 = begin(); auto last1 = end(); auto first2 = other.begin(); auto last2 = other.end(); while (first1 != last1 && first2 != last2) { if (comp(*first2, *first1)) { auto next = std::next(first2); first1._node->push_before(first2._node); ++_size; first2 = next; } else { ++first1; } } while (first2 != last2) { auto next = std::next(first2); _tail.push_before(first2._node); ++_size; first2 = next; } other._size = 0; } template constexpr void const_list::splice(const_list::const_iterator pos, const_list &other) { if (other._size == 0) return; for (auto& value : other) { value._owner = this; } pos->_prev->_next = other._tail->_next; pos->_prev = other._tail->_prev; _size += other._size; other._tail->_prev = &other._tail; other._tail->_next = &other._tail; other._size = 0; } template constexpr void const_list::splice(const_list::const_iterator pos, const_list &&other) { if (other._size == 0) return; for (auto& value : other) { value._owner = this; } pos->_prev->_next = other._tail->_next; pos->_prev = other._tail->_prev; _size += other._size; other._tail->_prev = &other._tail; other._tail->_next = &other._tail; } template constexpr void const_list::splice(const_list::const_iterator pos, const_list &other, const_list::const_iterator it) { // if it does point into other, then it has to have a size pos._const_cast()->push_before(it._const_cast()._node); ++_size; --other._size; } template constexpr void const_list::splice(const_list::const_iterator pos, const_list &&other, const_list::const_iterator it) { pos._const_cast()->push_before(it._const_cast()._node); ++_size; } template constexpr void const_list::splice(const_list::const_iterator pos, const_list &other, const_list::const_iterator first, const_list::const_iterator last) { auto p = pos._const_cast(); auto f = first._const_cast(); auto l = last._const_cast(); std::size_t dist = 0; for (auto it = f; it != l; ++it) { it._node->_owner = this; ++dist; } // we cannot use operator->, since it returns nullptr if it is a tail node auto first_prev = f._node->_prev; p._node->_prev->_next = f._node; f._node->_prev->_next = l._node; f._node->_prev = p._node->_prev; p._node->_prev = l._node->_prev; l._node->_prev->_next = p._node; l._node->_prev = first_prev; _size += dist; other._size -= dist; } template constexpr void const_list::splice(const_list::const_iterator pos, const_list &&other, const_list::const_iterator first, const_list::const_iterator last) { auto f = first._const_cast(); auto l = last._const_cast(); std::size_t dist = 0; for (auto it = f; it != l; ++it) { it->_owner = this; ++dist; } auto first_prev = first->_prev; pos->_prev->_next = f._node; f->_prev->_next = l._node; f->_prev = pos->_prev; pos->_prev = l->_prev; l->_prev->_next = pos._node; l->_prev = first_prev; _size += dist; } template constexpr const_list_node::const_list_node(const const_list_node &other) noexcept : _delete_cb(other._delete_cb) {} template constexpr const_list_node::const_list_node(const_list_node &&other) noexcept : _prev(other._prev), _next(other._next), _delete_cb(other._delete_cb) { _prev->_next = this; _next->_prev = this; // without this the destructor of other would delete moved nodes other._next = std::addressof(other); other._prev = std::addressof(other); } template constexpr const_list_node::~const_list_node() { if (_delete_cb) _delete_cb(); unlink(); } template constexpr const_list_node &const_list_node::operator=(const const_list_node &other) noexcept { return *this; } template constexpr const_list_node &const_list_node::operator=(const_list_node &&other) noexcept { _prev = other._prev; _next = other._next; _delete_cb = other._delete_cb; _prev->_next = this; _next->_prev = this; // without this the destructor of other would delete moved nodes other._next = std::addressof(other); other._prev = std::addressof(other); return *this; } template constexpr void const_list_node::on_delete(void (*cb)()) noexcept { _delete_cb = cb; } template constexpr void const_list_node::unlink() noexcept { if (_delete_cb) _delete_cb(); _prev->_next = _next; _next->_prev = _prev; _next = this; _prev = this; _owner = nullptr; } template constexpr void const_list_node::push_before(const_list_node *node) noexcept { node->_prev->_next = node->_next; node->_next->_prev = node->_prev; node->_prev = _prev; node->_next = this; _prev->_next = node; _prev = node; node->_owner = _owner; } } // cc #endif //UDIFF_CONST_LIST_H_