/** 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 node : public node_base{ T data; node* next(void)const{return static_cast(node_base::next);} node* prev(void)const{return static_cast(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 = node; 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}; } node_base* nod(void){return current;} 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 = node; const 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 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): node_allocator_type(alloc){} template REXY_CPP20_CONSTEXPR list::list(size_type count, const_reference value, const allocator_type& alloc): node_allocator_type(alloc) { assign(count, value); } template REXY_CPP20_CONSTEXPR list::list(size_type count, const allocator_type& alloc): node_allocator_type(alloc) { assign(count, value_type()); } template template REXY_CPP20_CONSTEXPR list::list(InputIt first, InputIt last, const allocator_type& alloc): node_allocator_type(alloc) { assign(first, last); } template REXY_CPP20_CONSTEXPR list::list(const list& other, const allocator_type& alloc): node_allocator_type(alloc) { assign(other.begin(), other.end()); } template REXY_CPP20_CONSTEXPR list::list(list&& other, const allocator_type& alloc): node_allocator_type(alloc) { swap(other); } template REXY_CPP20_CONSTEXPR list::list(std::initializer_list l, const allocator_type& alloc): node_allocator_type(alloc) { assign(l); } template REXY_CPP20_CONSTEXPR list::~list(void){ clear(); } template REXY_CPP20_CONSTEXPR auto list::operator=(const list& other) -> list&{ return (*this = list(other)); } template REXY_CPP20_CONSTEXPR auto list::operator=(list&& other) -> list&{ swap(other); return *this; } template REXY_CPP20_CONSTEXPR auto list::operator=(std::initializer_list l) -> 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){ assign(sized_constant_iterator{value, count}, sized_constant_iterator{{}, 0}); } template template REXY_CPP20_CONSTEXPR void list::assign(InputIt first, InputIt last){ auto current = begin(); size_type i = 0; 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){ 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) -> reference{return *begin();} template constexpr auto list::front(void)const -> const_reference{return *begin();} template constexpr auto list::back(void) -> reference{return *(--end());} template constexpr auto list::back(void)const -> 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{ erase(begin(), end()); } template REXY_CPP20_CONSTEXPR auto list::insert(const_iterator pos, const_reference value) -> iterator{ return insert(pos, value_type{value}); } template REXY_CPP20_CONSTEXPR auto list::insert(const_iterator pos, value_type&& value) -> iterator{ auto* prev = (--pos).unconst().nod(); auto* next = prev->next; prev->next = this->allocate(1); next->prev = prev->next; #ifdef __clang__ //clang won't let construct at emplace without creating a temporary here... std::construct_at(static_cast(prev->next), node{{next, prev}, std::move(value)}); #else std::construct_at(static_cast(prev->next), detail::node_base{next, prev}, std::move(value)); #endif ++m_size; return iterator{prev->next}; } template REXY_CPP20_CONSTEXPR auto list::insert(const_iterator pos, size_type count, const_reference value) -> 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) -> 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) -> iterator{ return insert(pos, l.begin(), l.end()); } template template REXY_CPP20_CONSTEXPR auto list::emplace(const_iterator pos, Args&&... args) -> iterator{ auto* prev = (--pos).unconst().nod(); auto* next = prev->next; prev->next = this->allocate(1); next->prev = prev->next; #ifdef __clang__ std::construct_at(static_cast(prev->next), node{{next, prev}, value_type{std::forward(args)...}}); #else std::construct_at(static_cast(prev->next), detail::node_base{next, prev}, value_type{std::forward(args)...}); #endif ++m_size; return iterator{prev->next}; } template REXY_CPP20_CONSTEXPR auto list::erase(const_iterator pos) -> 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) -> iterator{ while(first != last){ first = erase(first); } return last.unconst(); } template REXY_CPP20_CONSTEXPR auto list::push_back(const_reference value) -> reference{ return insert(cend(), value); } template REXY_CPP20_CONSTEXPR auto list::push_back(value_type&& value) -> reference{ return insert(cend(), std::move(value)); } template template REXY_CPP20_CONSTEXPR auto list::emplace_back(Args&&... args) -> reference{ return emplace(cend(), std::forward(args)...); } template REXY_CPP20_CONSTEXPR void list::pop_back(void){ erase(--cend()); } template REXY_CPP20_CONSTEXPR auto list::push_front(const_reference value) -> reference{ return insert(cbegin(), value); } template REXY_CPP20_CONSTEXPR auto list::push_front(value_type&& value) -> reference{ return insert(cbegin(), std::move(value)); } template template REXY_CPP20_CONSTEXPR auto list::emplace_front(Args&&... args) -> reference{ return emplace(cbegin(), std::forward(args)...); } template REXY_CPP20_CONSTEXPR void list::pop_front(void){ erase(cbegin()); } template REXY_CPP20_CONSTEXPR void list::resize(size_type count){ resize(count, value_type{}); } template REXY_CPP20_CONSTEXPR void list::resize(size_type count, value_type value){ while(count > m_size){ insert(cend(), value); } while(m_size > count){ erase(--cend()); } } template REXY_CPP20_CONSTEXPR void list::swap(list& other){ std::swap(m_sentinel, other.m_sentinel); std::swap(m_size, other.m_size); } template REXY_CPP20_CONSTEXPR void list::merge(list&& other){ 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){ 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){ splice(pos, std::move(other), other.begin(), other.end()); } template REXY_CPP20_CONSTEXPR void list::splice(const_iterator pos, list&& other, const_iterator it){ 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){ 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) -> size_type{ return remove_if( [&value](const_reference r) -> bool{ return r == value; } ); } template template REXY_CPP20_CONSTEXPR auto list::remove_if(UnaryPred pred) -> 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) -> size_type{ return unique( [](const_reference first, const_reference second) -> bool{ return first == second; } ); } template template REXY_CPP20_CONSTEXPR auto list::unique(BinaryPred pred) -> 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){ mergesort(begin(), m_size, [](const_reference first, const_reference second)->bool{ return first < second; }); } template template REXY_CPP20_CONSTEXPR void list::sort(Comp comp){ mergesort(begin(), m_size, comp); } template template auto list::mergesort(iterator first, size_type firstlen, Comp comp) -> 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) -> 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::node_base* prev, detail::node_base* n){ n->next = prev->next; n->prev = prev; prev->next->prev = n; prev->next = n; } template void list::remove_node_(detail::node_base* rm){ rm->prev->next = rm->next; rm->next->prev = rm->prev; } template detail::node_base* list::get_next_then_move_node_(detail::node_base* dest, detail::node_base* n){ auto* next = n->next; remove_node_(n); insert_node_(dest, n); return next; } } #endif