/**
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