rexylib/include/rexy/list.tpp

686 lines
22 KiB
C++

/**
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 <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_LLIST_TPP
#define REXY_LLIST_TPP
#include <utility> //move, swap, forward
#include <limits> //numeric_limits
#include <algorithm> //lexicographical_compare_three_way, equal
#include <memory> //construct_at, destroy_at
#include "utility.hpp" //sized_constant_iterator
namespace rexy{
namespace detail{
template<class T>
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<class... Args>
constexpr list_node(list_node_base* next, list_node_base* prev, Args&&... args):
list_node_base{next, prev},
data(std::forward<Args>(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*>(list_node_base::next);}
constexpr list_node* prev(void)const{return static_cast<list_node*>(list_node_base::prev);}
};
template<class T>
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<T>;
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<T>& other)const noexcept{return current == other.nod();}
constexpr bool operator!=(const const_list_iterator<T>& other)const noexcept{return current != other.nod();}
constexpr reference operator*(void){return static_cast<node_t*>(current)->data;}
constexpr const_reference operator*(void)const{return static_cast<const node_t*>(current)->data;}
constexpr pointer operator->(void){return &(static_cast<node_t*>(current)->data);}
constexpr const_pointer operator->(void)const{return &(static_cast<const node_t*>(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<T>(void)const{
return const_list_iterator<T>{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<class T>
struct const_list_iterator{
template<class U, class Alloc>
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<T>;
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<T>& other)const noexcept{return current == other.nod();}
constexpr bool operator!=(const list_iterator<T>& other)const noexcept{return current != other.nod();}
constexpr const_reference operator*(void){return static_cast<const node_t*>(current)->data;}
constexpr const_reference operator*(void)const{return static_cast<const node_t*>(current)->data;}
constexpr const_pointer operator->(void){return &(static_cast<const node_t*>(current)->data);}
constexpr const_pointer operator->(void)const{return &(static_cast<const node_t*>(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<T> unconst(void){
return list_iterator<T>{const_cast<list_node_base*>(current)};
}
};
}
namespace detail{
}
template<class T, class Alloc>
constexpr list<T,Alloc>::list(const allocator_type& alloc):
node_allocator_type(alloc){}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR list<T,Alloc>::list(size_type count, const_reference value, const allocator_type& alloc):
node_allocator_type(alloc)
{
constant_initialize_(count, value);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR list<T,Alloc>::list(size_type count, const allocator_type& alloc):
node_allocator_type(alloc)
{
default_initialize_(count);
}
template<class T, class Alloc>
template<class InputIt>
REXY_CPP20_CONSTEXPR list<T,Alloc>::list(InputIt first, InputIt last, const allocator_type& alloc):
node_allocator_type(alloc)
{
iterator_initialize_(first, last);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR list<T,Alloc>::list(const list& other, const allocator_type& alloc):
node_allocator_type(alloc)
{
iterator_initialize_(other.begin(), other.end());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR list<T,Alloc>::list(list&& other, const allocator_type& alloc):
node_allocator_type(alloc)
{
swap(other);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR list<T,Alloc>::list(std::initializer_list<value_type> l, const allocator_type& alloc):
node_allocator_type(alloc)
{
iterator_initialize_(l.begin(), l.end());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR list<T,Alloc>::~list(void){
clear();
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::operator=(const list& other) -> list&{
return (*this = list(other));
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::operator=(list&& other) -> list&{
swap(other);
return *this;
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::operator=(std::initializer_list<value_type> l) -> list&{
return (*this = list(l));
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::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<class T, class Alloc>
constexpr auto list<T,Alloc>::operator<=>(const list& other)const noexcept{
return std::lexicographical_compare_three_way(cbegin(), cend(), other.cbegin(), other.cend());
}
#else
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator!=(const list& other)const noexcept{
return !(*this == other);
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator<(const list& other)const noexcept{
return std::lexicographical_compare(cbegin(), cend(), other.cbegin(), other.cend());
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator<=(const list& other)const noexcept{
return !(other < *this);
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator>(const list& other)const noexcept{
return other < *this;
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator>=(const list& other)const noexcept{
return !(*this < other);
}
#endif
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::assign(size_type count, const_reference value){
assign(sized_constant_iterator{value, count}, sized_constant_iterator<value_type>{{}, 0});
}
template<class T, class Alloc>
template<class InputIt>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::iterator_initialize_(InputIt first, InputIt last){
for(;first != last;++first){
emplace_back(*first);
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::constant_initialize_(size_type count, const_reference value){
for(;count > 0;--count){
emplace_back(value);
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::default_initialize_(size_type count){
for(;count > 0;--count){
emplace_back();
}
}
template<class T, class Alloc>
template<class InputIt>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::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<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::assign(std::initializer_list<value_type> l){
assign(l.begin(), l.end());
}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::get_allocator(void)const noexcept -> allocator_type{return {*this};}
//Direct accessors
template<class T, class Alloc>
constexpr auto list<T,Alloc>::front(void) -> reference{return *begin();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::front(void)const -> const_reference{return *begin();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::back(void) -> reference{return *(--end());}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::back(void)const -> const_reference{return *(--end());}
//Iterators
template<class T, class Alloc>
constexpr auto list<T,Alloc>::begin(void)noexcept -> iterator{return iterator{m_sentinel.next};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::begin(void)const noexcept -> const_iterator{return const_iterator{m_sentinel.next};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::cbegin(void)const noexcept -> const_iterator{return const_iterator{m_sentinel.next};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::end(void)noexcept -> iterator{return iterator{&m_sentinel};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::end(void)const noexcept -> const_iterator{return const_iterator{&m_sentinel};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::cend(void)const noexcept -> const_iterator{return const_iterator{&m_sentinel};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::next(iterator i)noexcept -> iterator{
return i.next();
}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::next(const_iterator i)noexcept -> const_iterator{return i.next();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::prev(iterator i)noexcept -> iterator{return i.prev();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::prev(const_iterator i)noexcept -> const_iterator{return i.prev();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::rbegin(void)noexcept -> reverse_iterator{return reverse_iterator{end()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::rbegin(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{end()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::crbegin(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{end()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::rend(void)noexcept -> reverse_iterator{return reverse_iterator{begin()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::rend(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{begin()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::crend(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{begin()};}
//Queries
template<class T, class Alloc>
constexpr bool list<T,Alloc>::empty(void)const noexcept{return m_sentinel.next == nullptr;}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::size(void)const noexcept -> size_type{return m_size;}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::max_size(void)const noexcept -> size_type{return std::numeric_limits<difference_type>::max();}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::clear(void)noexcept{
erase(begin(), end());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::insert(const_iterator pos, const_reference value) -> iterator{
return insert(pos, value_type{value});
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::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;
std::construct_at(static_cast<node*>(prev->next), next, prev, std::move(value));
++m_size;
return iterator{prev->next};
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::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<class T, class Alloc>
template<class InputIt>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::insert(const_iterator pos, InputIt first, InputIt last) -> iterator{
auto start = pos.unconst();
while(first != last){
pos = ++(insert(pos, *first++));
}
return start;
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::insert(const_iterator pos, std::initializer_list<value_type> l) -> iterator{
return insert(pos, l.begin(), l.end());
}
template<class T, class Alloc>
template<class... Args>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::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;
std::construct_at(static_cast<node*>(prev->next), next, prev, std::forward<Args>(args)...);
++m_size;
return iterator{prev->next};
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::erase(const_iterator pos) -> iterator{
auto* n = pos.unconst().nod();
auto* next = n->next;
remove_node_(n);
std::destroy_at(static_cast<node*>(n));
this->deallocate(static_cast<node*>(n), 1);
--m_size;
return iterator{next};
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::erase(const_iterator first, const_iterator last) -> iterator{
while(first != last){
first = erase(first);
}
return last.unconst();
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::push_back(const_reference value) -> reference{
return *insert(cend(), value);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::push_back(value_type&& value) -> reference{
return *insert(cend(), std::move(value));
}
template<class T, class Alloc>
template<class... Args>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::emplace_back(Args&&... args) -> reference{
return *emplace(cend(), std::forward<Args>(args)...);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::pop_back(void){
erase(--cend());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::push_front(const_reference value) -> reference{
return insert(cbegin(), value);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::push_front(value_type&& value) -> reference{
return insert(cbegin(), std::move(value));
}
template<class T, class Alloc>
template<class... Args>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::emplace_front(Args&&... args) -> reference{
return emplace(cbegin(), std::forward<Args>(args)...);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::pop_front(void){
erase(cbegin());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::resize(size_type count){
resize(count, value_type{});
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::resize(size_type count, value_type value){
while(count > m_size){
insert(cend(), value);
}
while(m_size > count){
erase(--cend());
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::swap(list& other){
std::swap(m_sentinel, other.m_sentinel);
std::swap(m_size, other.m_size);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::merge(list&& other){
merge(std::move(other), [](const_reference a, const_reference b){return a < b;});
}
template<class T, class Alloc>
template<class Comp>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::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<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::splice(const_iterator pos, list&& other){
splice(pos, std::move(other), other.begin(), other.end());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::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<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::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<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::remove(const_reference value) -> size_type{
return remove_if(
[&value](const_reference r) -> bool{
return r == value;
}
);
}
template<class T, class Alloc>
template<class UnaryPred>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::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<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::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<class T, class Alloc>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::unique(void) -> size_type{
return unique(
[](const_reference first, const_reference second) -> bool{
return first == second;
}
);
}
template<class T, class Alloc>
template<class BinaryPred>
REXY_CPP20_CONSTEXPR auto list<T,Alloc>::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<class T, class Alloc>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::sort(void){
mergesort(begin(), m_size, [](const_reference first, const_reference second)->bool{
return first < second;
});
}
template<class T, class Alloc>
template<class Comp>
REXY_CPP20_CONSTEXPR void list<T,Alloc>::sort(Comp comp){
mergesort(begin(), m_size, comp);
}
template<class T, class Alloc>
template<class Comp>
auto list<T,Alloc>::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<class T, class Alloc>
auto list<T,Alloc>::ms_split(iterator it, size_type len) -> std::pair<iterator,size_type>{
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<class T, class Alloc>
void list<T,Alloc>::insert_node_(detail::list_node_base* prev, detail::list_node_base* n){
n->next = prev->next;
n->prev = prev;
prev->next->prev = n;
prev->next = n;
}
template<class T, class Alloc>
void list<T,Alloc>::remove_node_(detail::list_node_base* rm){
rm->prev->next = rm->next;
rm->next->prev = rm->prev;
}
template<class T, class Alloc>
detail::list_node_base* list<T,Alloc>::get_next_then_move_node_(detail::list_node_base* dest, detail::list_node_base* n){
auto* next = n->next;
remove_node_(n);
insert_node_(dest, n);
return next;
}
}
#endif