/** This file is a part of rexy's general purpose library Copyright (C) 2022 rexy712 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef REXY_LLIST_TPP #define REXY_LLIST_TPP #include //move, swap, forward #include //numeric_limits #include //lexicographical_compare_three_way, equal #include //construct_at, destroy_at #include "utility.hpp" //sized_constant_iterator namespace rexy{ namespace detail{ template struct list_node : public list_node_base{ T data; constexpr list_node(list_node_base* next, list_node_base* prev, const T& d): list_node_base{next, prev}, data(d){} constexpr list_node(list_node_base* next, list_node_base* prev, T&& d): list_node_base{next, prev}, data(std::move(d)){} template constexpr list_node(list_node_base* next, list_node_base* prev, Args&&... args): list_node_base{next, prev}, data(std::forward(args)...){} constexpr list_node(const list_node& n): list_node_base(n), data(n.data){} constexpr list_node(list_node&& n): list_node_base(std::move(n)), data(std::move(n.data)){} constexpr list_node* next(void)const{return static_cast(list_node_base::next);} constexpr list_node* prev(void)const{return static_cast(list_node_base::prev);} }; template struct list_iterator{ using value_type = T; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; using node_t = list_node; list_node_base* current = nullptr; constexpr bool operator==(const list_iterator& other)const noexcept{return current == other.current;} constexpr bool operator!=(const list_iterator& other)const noexcept{return current != other.current;} constexpr bool operator==(const const_list_iterator& other)const noexcept{return current == other.nod();} constexpr bool operator!=(const const_list_iterator& other)const noexcept{return current != other.nod();} constexpr reference operator*(void){return static_cast(current)->data;} constexpr const_reference operator*(void)const{return static_cast(current)->data;} constexpr pointer operator->(void){return &(static_cast(current)->data);} constexpr const_pointer operator->(void)const{return &(static_cast(current)->data);} constexpr list_iterator& operator++(void){ current = current->next; return *this; } constexpr list_iterator operator++(int){ list_iterator copy(*this); ++(*this); return copy; } constexpr list_iterator& operator--(void){ current = current->prev; return *this; } constexpr list_iterator operator--(int){ list_iterator copy(*this); --(*this); return copy; } constexpr operator const_list_iterator(void)const{ return const_list_iterator{current}; } list_iterator next(void)const{ return list_iterator{current->next}; } list_iterator prev(void)const{ return list_iterator{current->prev}; } list_node_base* nod(void){return current;} list_node_base* nod(void)const{return current;} }; template struct const_list_iterator{ template friend class rexy::list; using value_type = T; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; using node_t = list_node; const list_node_base* current = nullptr; constexpr bool operator==(const const_list_iterator& other)const noexcept{return current == other.current;} constexpr bool operator!=(const const_list_iterator& other)const noexcept{return current != other.current;} constexpr bool operator==(const list_iterator& other)const noexcept{return current == other.nod();} constexpr bool operator!=(const list_iterator& other)const noexcept{return current != other.nod();} constexpr const_reference operator*(void){return static_cast(current)->data;} constexpr const_reference operator*(void)const{return static_cast(current)->data;} constexpr const_pointer operator->(void){return &(static_cast(current)->data);} constexpr const_pointer operator->(void)const{return &(static_cast(current)->data);} constexpr const_list_iterator& operator++(void){ current = current->next; return *this; } constexpr const_list_iterator operator++(int){ const_list_iterator copy(*this); ++(*this); return copy; } constexpr const_list_iterator& operator--(void){ current = current->prev; return *this; } constexpr const_list_iterator operator--(int){ const_list_iterator copy(*this); --(*this); return copy; } const_list_iterator next(void)const{ return const_list_iterator{current->next}; } const_list_iterator prev(void)const{ return const_list_iterator{current->prev}; } const list_node_base* nod(void)const{return current;} protected: list_iterator unconst(void){ return list_iterator{const_cast(current)}; } }; } namespace detail{ } template constexpr list::list(const allocator_type& alloc)noexcept: node_allocator_type(alloc){} template REXY_CPP20_CONSTEXPR list::list(size_type count, const_reference value, const allocator_type& alloc)noexcept( std::is_nothrow_copy_constructible_v && is_nothrow_allocator_v): node_allocator_type(alloc) { constant_initialize_(count, value); } template REXY_CPP20_CONSTEXPR list::list(size_type count, const allocator_type& alloc)noexcept( std::is_nothrow_default_constructible_v && is_nothrow_allocator_v): node_allocator_type(alloc) { default_initialize_(count); } template template REXY_CPP20_CONSTEXPR list::list(InputIt first, InputIt last, const allocator_type& alloc)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v): node_allocator_type(alloc) { iterator_initialize_(first, last); } template REXY_CPP20_CONSTEXPR list::list(const list& other, const allocator_type& alloc)noexcept( std::is_nothrow_copy_constructible_v && is_nothrow_allocator_v): node_allocator_type(alloc) { iterator_initialize_(other.begin(), other.end()); } template REXY_CPP20_CONSTEXPR list::list(list&& other, const allocator_type& alloc)noexcept: node_allocator_type(alloc) { swap(other); } template REXY_CPP20_CONSTEXPR list::list(std::initializer_list l, const allocator_type& alloc)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v): node_allocator_type(alloc) { iterator_initialize_(l.begin(), l.end()); } template REXY_CPP20_CONSTEXPR list::~list(void)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) { clear(); } template REXY_CPP20_CONSTEXPR auto list::operator=(const list& other)noexcept( std::is_nothrow_copy_constructible_v && is_nothrow_allocator_v) -> list& { return (*this = list(other)); } template REXY_CPP20_CONSTEXPR auto list::operator=(list&& other)noexcept -> list&{ swap(other); return *this; } template REXY_CPP20_CONSTEXPR auto list::operator=(std::initializer_list l)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v) -> list& { return (*this = list(l)); } template constexpr bool list::operator==(const list& other)const noexcept{ if(m_size != other.m_size) return false; return std::equal(cbegin(), cend(), other.cbegin()); } #if __cpp_impl_three_way_comparison template constexpr auto list::operator<=>(const list& other)const noexcept{ return std::lexicographical_compare_three_way(cbegin(), cend(), other.cbegin(), other.cend()); } #else template constexpr bool list::operator!=(const list& other)const noexcept{ return !(*this == other); } template constexpr bool list::operator<(const list& other)const noexcept{ return std::lexicographical_compare(cbegin(), cend(), other.cbegin(), other.cend()); } template constexpr bool list::operator<=(const list& other)const noexcept{ return !(other < *this); } template constexpr bool list::operator>(const list& other)const noexcept{ return other < *this; } template constexpr bool list::operator>=(const list& other)const noexcept{ return !(*this < other); } #endif template REXY_CPP20_CONSTEXPR void list::assign(size_type count, const_reference value)noexcept( std::conditional_t< std::is_copy_assignable_v, std::is_nothrow_copy_assignable, std::true_type>::value && std::is_nothrow_copy_constructible_v && std::is_nothrow_destructible_v && is_nothrow_allocator_v) { assign(sized_constant_iterator{value, count}, sized_constant_iterator{{}, 0}); } template template REXY_CPP20_CONSTEXPR void list::assign(InputIt first, InputIt last)noexcept( std::conditional_t< std::is_assignable_v, std::is_nothrow_assignable, std::true_type>::value && std::is_nothrow_constructible_v && std::is_nothrow_destructible_v && is_nothrow_allocator_v) { auto current = begin(); size_type i = 0; if constexpr(std::is_assignable_v){ for(;i < m_size && first != last;++i){ *current++ = *first++; } } if(first != last){ m_size = i; while(first != last){ current = ++(insert(current, *first++)); } }else{ erase(current, end()); } } template REXY_CPP20_CONSTEXPR void list::assign(std::initializer_list l)noexcept( std::conditional_t< std::is_assignable_v, std::is_nothrow_assignable, std::true_type>::value && std::is_nothrow_constructible_v && std::is_nothrow_destructible_v && is_nothrow_allocator_v) { assign(l.begin(), l.end()); } template constexpr auto list::get_allocator(void)const noexcept -> allocator_type{return {*this};} //Direct accessors template constexpr auto list::front(void)noexcept -> reference{return *begin();} template constexpr auto list::front(void)const noexcept -> const_reference{return *begin();} template constexpr auto list::back(void)noexcept -> reference{return *(--end());} template constexpr auto list::back(void)const noexcept-> const_reference{return *(--end());} //Iterators template constexpr auto list::begin(void)noexcept -> iterator{return iterator{m_sentinel.next};} template constexpr auto list::begin(void)const noexcept -> const_iterator{return const_iterator{m_sentinel.next};} template constexpr auto list::cbegin(void)const noexcept -> const_iterator{return const_iterator{m_sentinel.next};} template constexpr auto list::end(void)noexcept -> iterator{return iterator{&m_sentinel};} template constexpr auto list::end(void)const noexcept -> const_iterator{return const_iterator{&m_sentinel};} template constexpr auto list::cend(void)const noexcept -> const_iterator{return const_iterator{&m_sentinel};} template constexpr auto list::next(iterator i)noexcept -> iterator{return i.next();} template constexpr auto list::next(const_iterator i)noexcept -> const_iterator{return i.next();} template constexpr auto list::prev(iterator i)noexcept -> iterator{return i.prev();} template constexpr auto list::prev(const_iterator i)noexcept -> const_iterator{return i.prev();} template constexpr auto list::rbegin(void)noexcept -> reverse_iterator{return reverse_iterator{end()};} template constexpr auto list::rbegin(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{end()};} template constexpr auto list::crbegin(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{end()};} template constexpr auto list::rend(void)noexcept -> reverse_iterator{return reverse_iterator{begin()};} template constexpr auto list::rend(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{begin()};} template constexpr auto list::crend(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{begin()};} //Queries template constexpr bool list::empty(void)const noexcept{return m_sentinel.next == nullptr;} template constexpr auto list::size(void)const noexcept -> size_type{return m_size;} template constexpr auto list::max_size(void)const noexcept -> size_type{return std::numeric_limits::max();} template REXY_CPP20_CONSTEXPR void list::clear(void)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) { erase(begin(), end()); } template REXY_CPP20_CONSTEXPR auto list::insert(const_iterator pos, const_reference value)noexcept( std::is_nothrow_copy_constructible_v && is_nothrow_allocator_v) -> iterator { return insert(pos, value_type{value}); } template REXY_CPP20_CONSTEXPR auto list::insert(const_iterator pos, value_type&& value)noexcept( std::is_nothrow_move_constructible_v && is_nothrow_allocator_v) -> iterator { auto* prev = (--pos).unconst().nod(); auto* next = prev->next; prev->next = this->allocate(1); next->prev = prev->next; std::construct_at(static_cast(prev->next), next, prev, std::move(value)); ++m_size; return iterator{prev->next}; } template REXY_CPP20_CONSTEXPR auto list::insert(const_iterator pos, size_type count, const_reference value)noexcept( std::is_nothrow_copy_constructible_v && is_nothrow_allocator_v) -> iterator { auto start = pos.unconst(); for(auto i = count;i > 0;--i){ pos = ++(insert(pos, value_type{value})); } return start; } template template REXY_CPP20_CONSTEXPR auto list::insert(const_iterator pos, InputIt first, InputIt last)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v) -> iterator { auto start = pos.unconst(); while(first != last){ pos = ++(insert(pos, *first++)); } return start; } template REXY_CPP20_CONSTEXPR auto list::insert(const_iterator pos, std::initializer_list l)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v) -> iterator { return insert(pos, l.begin(), l.end()); } template template REXY_CPP20_CONSTEXPR auto list::emplace(const_iterator pos, Args&&... args)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v) -> iterator { auto* prev = (--pos).unconst().nod(); auto* next = prev->next; prev->next = this->allocate(1); next->prev = prev->next; std::construct_at(static_cast(prev->next), next, prev, std::forward(args)...); ++m_size; return iterator{prev->next}; } template REXY_CPP20_CONSTEXPR auto list::erase(const_iterator pos)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) -> iterator { auto* n = pos.unconst().nod(); auto* next = n->next; remove_node_(n); std::destroy_at(static_cast(n)); this->deallocate(static_cast(n), 1); --m_size; return iterator{next}; } template REXY_CPP20_CONSTEXPR auto list::erase(const_iterator first, const_iterator last)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) -> iterator { while(first != last){ first = erase(first); } return last.unconst(); } template REXY_CPP20_CONSTEXPR auto list::push_back(const_reference value)noexcept( std::is_nothrow_copy_constructible_v && is_nothrow_allocator_v) -> reference { return *insert(cend(), value); } template REXY_CPP20_CONSTEXPR auto list::push_back(value_type&& value)noexcept( std::is_nothrow_move_constructible_v && is_nothrow_allocator_v) -> reference { return *insert(cend(), std::move(value)); } template template REXY_CPP20_CONSTEXPR auto list::emplace_back(Args&&... args)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v) -> reference { return *emplace(cend(), std::forward(args)...); } template REXY_CPP20_CONSTEXPR void list::pop_back(void)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) { erase(--cend()); } template REXY_CPP20_CONSTEXPR auto list::push_front(const_reference value)noexcept( std::is_nothrow_copy_constructible_v && is_nothrow_allocator_v) -> reference { return insert(cbegin(), value); } template REXY_CPP20_CONSTEXPR auto list::push_front(value_type&& value)noexcept( std::is_nothrow_move_constructible_v && is_nothrow_allocator_v) -> reference { return insert(cbegin(), std::move(value)); } template template REXY_CPP20_CONSTEXPR auto list::emplace_front(Args&&... args)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v) -> reference { return emplace(cbegin(), std::forward(args)...); } template REXY_CPP20_CONSTEXPR void list::pop_front(void)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) { erase(cbegin()); } template REXY_CPP20_CONSTEXPR void list::resize(size_type count)noexcept( std::is_nothrow_default_constructible_v && std::is_nothrow_destructible_v && is_nothrow_allocator_v) { resize(count, value_type{}); } template REXY_CPP20_CONSTEXPR void list::resize(size_type count, value_type value)noexcept( std::is_nothrow_copy_constructible_v && std::is_nothrow_destructible_v && is_nothrow_allocator_v) { while(count > m_size){ insert(cend(), value); } while(m_size > count){ erase(--cend()); } } template REXY_CPP20_CONSTEXPR void list::swap(list& other)noexcept{ std::swap(m_sentinel, other.m_sentinel); std::swap(m_size, other.m_size); } template REXY_CPP20_CONSTEXPR void list::merge(list&& other)noexcept{ merge(std::move(other), [](const_reference a, const_reference b){return a < b;}); } template template REXY_CPP20_CONSTEXPR void list::merge(list&& other, Comp comp)noexcept{ if(&other == this){ return; } auto it = begin(); auto oit = other.begin(); while(it != end() && oit != other.end()){ if(comp(*oit, *it)){ oit = iterator{get_next_then_move_node_(it.prev().nod(), oit.nod())}; ++m_size; }else{ ++it; } } splice(it, std::move(other)); } template REXY_CPP20_CONSTEXPR void list::splice(const_iterator pos, list&& other)noexcept{ splice(pos, std::move(other), other.begin(), other.end()); } template REXY_CPP20_CONSTEXPR void list::splice(const_iterator pos, list&& other, const_iterator it)noexcept{ auto oit = it.unconst(); auto prev = (--pos).unconst(); remove_node_(oit.nod()); insert_node_(prev.nod(), oit.nod()); ++m_size; --other.m_size; } template REXY_CPP20_CONSTEXPR void list::splice(const_iterator pos, list&& other, const_iterator first, const_iterator last)noexcept{ for(auto oit = first;oit != last;){ auto onext = oit.next(); splice(pos, std::move(other), oit); oit = onext; } } template REXY_CPP20_CONSTEXPR auto list::remove(const_reference value)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) -> size_type { return remove_if( [&value](const_reference r) -> bool{ return r == value; } ); } template template REXY_CPP20_CONSTEXPR auto list::remove_if(UnaryPred pred)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) -> size_type { size_type count = 0; for(auto it = begin();it != end();){ if(pred(*it)){ it = erase(it); ++count; }else{ ++it; } } return count; } template REXY_CPP20_CONSTEXPR void list::reverse(void)noexcept{ auto* it = begin().nod(); std::swap(m_sentinel.next, m_sentinel.prev); for(;it != &m_sentinel;it = it->prev){ std::swap(it->next, it->prev); } } template REXY_CPP20_CONSTEXPR auto list::unique(void)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) -> size_type { return unique( [](const_reference first, const_reference second) -> bool{ return first == second; } ); } template template REXY_CPP20_CONSTEXPR auto list::unique(BinaryPred pred)noexcept( std::is_nothrow_destructible_v && is_nothrow_allocator_v) -> size_type { size_type count = 0; for(auto it = begin();it != end();++it){ auto next = it.next(); while(next != end() && pred(*it, *next)){ next = erase(next); ++count; } } return count; } template REXY_CPP20_CONSTEXPR void list::sort(void)noexcept{ mergesort(begin(), m_size, [](const_reference first, const_reference second)->bool{ return first < second; }); } template template REXY_CPP20_CONSTEXPR void list::sort(Comp comp)noexcept{ mergesort(begin(), m_size, comp); } template template REXY_CPP20_CONSTEXPR void list::iterator_initialize_(InputIt first, InputIt last)noexcept( std::is_nothrow_constructible_v && is_nothrow_allocator_v) { for(;first != last;++first){ emplace_back(*first); } } template REXY_CPP20_CONSTEXPR void list::constant_initialize_(size_type count, const_reference value)noexcept( std::is_nothrow_copy_constructible_v && is_nothrow_allocator_v) { for(;count > 0;--count){ emplace_back(value); } } template REXY_CPP20_CONSTEXPR void list::default_initialize_(size_type count)noexcept( std::is_nothrow_default_constructible_v && is_nothrow_allocator_v) { for(;count > 0;--count){ emplace_back(); } } template template auto list::mergesort(iterator first, size_type firstlen, Comp comp)noexcept -> iterator{ if(firstlen == 1) return first; auto [second, secondlen] = ms_split(first, firstlen); firstlen -= secondlen; first = mergesort(first, firstlen, comp); second = mergesort(second, secondlen, comp); iterator result = first; //do this outside the loop to save return value if(comp(*second, *first)){ result = second; second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())}; --secondlen; } while(firstlen > 0 && secondlen > 0){ if(comp(*second, *first)){ second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())}; --secondlen; }else{ ++first; --firstlen; } } //finish off the second list if it isn't already done while(secondlen > 0){ second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())}; --secondlen; } return result; } template auto list::ms_split(iterator it, size_type len)noexcept -> std::pair{ size_type second_half_len = len / 2; size_type dist = len - second_half_len; for(auto i = dist;i > 0;--i){ ++it; } return {it, second_half_len}; } template void list::insert_node_(detail::list_node_base* prev, detail::list_node_base* n)noexcept{ n->next = prev->next; n->prev = prev; prev->next->prev = n; prev->next = n; } template void list::remove_node_(detail::list_node_base* rm)noexcept{ rm->prev->next = rm->next; rm->next->prev = rm->prev; } template detail::list_node_base* list::get_next_then_move_node_(detail::list_node_base* dest, detail::list_node_base* n)noexcept{ auto* next = n->next; remove_node_(n); insert_node_(dest, n); return next; } } #endif