17#include <initializer_list>
111 friend class List<T>;
116 std::unique_ptr<NodeBase> m_next{};
156 [[nodiscard]]
auto value() const -> T
181 template<
bool IF_CONST>
239 : m_current_node{ node }
251 this->m_current_node = node;
264 m_current_node = m_current_node->m_next.get();
291 m_current_node = m_current_node->m_prev;
319 return m_current_node == other.m_current_node;
347 for (
size_t i = 0; i < index; i++)
351 temp = temp->m_next.get();
369 if (
Node* node =
dynamic_cast<Node*
>(m_current_node))
371 return node->value();
373 throw std::runtime_error(
"Invalid iterator dereference");
383 if (
Node* node =
dynamic_cast<Node*
>(m_current_node))
385 return &node->value();
387 throw std::runtime_error(
"Invalid iterator pointer");
473 List(
size_t count,
const T& value);
480 List(
const std::initializer_list<T>& init_list);
532 void assign(
const std::initializer_list<T>& init_list);
610 [[nodiscard]] auto
empty() const ->
bool;
617 [[nodiscard]] auto
size() const ->
size_t;
624 [[nodiscard]] auto
max_size() const ->
size_t;
740 void resize(
size_t count);
862 for (
auto it = other.cbegin(); it != other.cend(); ++it)
878 for (
const auto& item : init_list)
894 [[nodiscard]]
auto get(
size_t index)
const -> Node*;
905 auto set(
size_t index, T value) -> bool;
927 if (m_head ==
nullptr)
929 m_head = std::make_unique<NodeBase>();
930 m_tail = m_head.get();
949 if (pos == (--
end()))
955 NodeBase* temp{ pos.m_current_node->m_prev };
956 NodeBase* to_remove{ temp->m_next.get() };
958 temp->m_next = std::move(to_remove->m_next);
959 temp->m_next->m_prev = temp;
962 return iterator(temp->m_next.get());
985 return end().m_current_node->m_prev;
988 NodeBase* temp{ pos.m_current_node->m_prev };
990 auto newNode = std::make_unique<Node>(value);
991 newNode->m_next = std::move(temp->m_next);
992 newNode->m_prev = temp;
994 temp->m_next = std::move(newNode);
995 temp->m_next->m_next->m_prev = temp->m_next.get();
998 return iterator(temp->m_next.get());
1011 bool valid_iterator{};
1022 valid_iterator =
true;
1026 return valid_iterator;
1052 std::unique_ptr<NodeBase> m_head{};
1057 template<
typename T>
1063 template<
typename T>
1069 template<
typename T>
1074 for (
size_t i = 0; i < count; i++)
1080 template<
typename T>
1083 for (
const auto& item : init_list)
1089 template<
typename T>
1092 for (
size_t i = 0; i < other.
size(); i++)
1098 template<
typename T>
1103 while (m_head->m_next)
1108 for (
size_t i = 0; i < other.size(); i++)
1117 template<
typename T>
1123 template<
typename T>
1130 m_head = std::move(other.m_head);
1131 m_tail = std::move(other.m_tail);
1132 m_size = other.m_size;
1134 other.m_head =
nullptr;
1135 other.m_tail =
nullptr;
1142 template<
typename T>
1148 template<
typename T>
1153 for (
size_t i = 0; i < count; i++)
1159 template<
typename T>
1164 for (
const auto& item : init_list)
1170 template<
typename T>
1176 template<
typename T>
1182 template<
typename T>
1188 template<
typename T>
1194 template<
typename T>
1200 template<
typename T>
1206 template<
typename T>
1212 template<
typename T>
1218 template<
typename T>
1224 template<
typename T>
1230 template<
typename T>
1236 template<
typename T>
1242 template<
typename T>
1245 return std::numeric_limits<size_t>::max();
1248 template<
typename T>
1251 if (m_head && m_head->m_next)
1253 while (m_head->m_next)
1255 m_head = std::move(m_head->m_next);
1259 m_tail->m_prev =
nullptr;
1263 template<
typename T>
1266 return insert(pos, 1, value);
1269 template<
typename T>
1272 iterator iter{ pos.m_current_node };
1274 if (!if_valid_iterator(pos))
1279 for (
size_t i = 0; i < count; i++)
1281 iter = insert_element_before(iter, value);
1287 template<
typename T>
1292 if (!if_valid_iterator(pos))
1297 for (
const auto& val : init_list)
1299 iter = insert_element_before(iter, val);
1306 template<
typename T>
1309 if (!if_valid_iterator(pos))
1314 return erase_element(pos);
1317 template<
typename T>
1320 if (!if_valid_iterator(first) || !if_valid_iterator(last))
1325 const size_t dist = distance(first, last);
1331 iterator iter(first.m_current_node);
1332 for (
size_t i = 0; i < dist; i++)
1334 iter = erase_element(iter);
1340 template<
typename T>
1345 auto newNode = std::make_unique<Node>(value);
1346 if (!m_head->m_next)
1348 newNode->m_next = std::move(m_head);
1349 m_head = std::move(newNode);
1350 m_tail->m_prev = m_head.get();
1354 m_head->m_prev = newNode.get();
1355 newNode->m_next = std::move(m_head);
1356 m_head = std::move(newNode);
1362 template<
typename T>
1372 m_head = std::move(m_head->m_next);
1373 m_tail->m_prev =
nullptr;
1377 m_head = std::move(m_head->m_next);
1378 m_head->m_prev =
nullptr;
1384 template<
typename T>
1389 auto newNode = std::make_unique<Node>(value);
1391 if (!m_head->m_next)
1393 newNode->m_next = std::move(m_head);
1394 m_head = std::move(newNode);
1395 m_tail->m_prev = m_head.get();
1399 newNode->m_prev = m_tail->m_prev;
1400 newNode->m_next = std::move(m_tail->m_prev->m_next);
1401 m_tail->m_prev = newNode.get();
1402 m_tail->m_prev->m_prev->m_next = std::move(newNode);
1408 template<
typename T>
1418 m_head = std::move(m_head->m_next);
1419 m_tail->m_prev =
nullptr;
1423 NodeBase* temp{ m_tail->m_prev->m_prev };
1424 m_tail->m_prev->m_prev->m_next = std::move(m_tail->m_prev->m_next);
1425 m_tail->m_prev = temp;
1431 template<
typename T>
1437 template<
typename T>
1440 if (count == m_size)
1448 while (m_size > count)
1457 while (m_size < count)
1464 template<
typename T>
1467 std::swap(m_head, other.m_head);
1468 std::swap(m_tail, other.m_tail);
1469 std::swap(m_size, other.m_size);
1472 template<
typename T>
1475 merge(std::move(other));
1478 template<
typename T>
1487 auto temp_head = std::make_unique<Node>(0);
1488 NodeBase* temp_tail = temp_head.get();
1490 std::unique_ptr<NodeBase> to_move{};
1491 std::unique_ptr<NodeBase> to_return{};
1493 std::unique_ptr<NodeBase> sentinel_this{ std::move(m_tail->m_prev->m_next) };
1494 const std::unique_ptr<NodeBase> sentinel_other{ std::move(other.m_tail->m_prev->m_next) };
1496 while (m_head && other.m_head)
1498 Node* node_this =
dynamic_cast<Node*
>(m_head.get());
1499 Node* node_other =
dynamic_cast<Node*
>(other.m_head.get());
1501 if (node_this && node_other)
1503 if (node_this->
value() <= node_other->
value())
1505 to_move = std::move(m_head);
1506 to_move->m_prev = temp_tail;
1508 to_return = std::move(to_move->m_next);
1509 temp_tail->m_next = std::move(to_move);
1510 m_head = std::move(to_return);
1514 to_move = std::move(other.m_head);
1515 to_move->m_prev = temp_tail;
1517 to_return = std::move(to_move->m_next);
1518 temp_tail->m_next = std::move(to_move);
1519 other.m_head = std::move(to_return);
1522 temp_tail = temp_tail->m_next.get();
1527 if (m_head ==
nullptr)
1529 last = sentinel_other->m_prev;
1530 other.m_head->m_prev = temp_tail;
1531 temp_tail->m_next = std::move(other.m_head);
1535 last = sentinel_this->m_prev;
1536 m_head->m_prev = temp_tail;
1537 temp_tail->m_next = std::move(m_head);
1539 last->m_next = std::move(sentinel_this);
1540 last->m_next->m_prev = last;
1542 m_head = std::move(temp_head->m_next);
1543 m_head->m_prev =
nullptr;
1545 m_size += other.m_size;
1555 template<
typename T>
1556 auto List<T>::distance(const_iterator first,
const const_iterator& last) ->
size_t
1559 while (first != last)
1568 template<
typename T>
1571 void List<T>::transfer(const_iterator pos,
List<T>&& other, const_iterator first,
const const_iterator& last)
1573 if (&other !=
this && other.m_size > 0)
1575 const size_t dist = distance(first, last);
1581 NodeBase* temp_prev{ pos.m_current_node->m_prev };
1582 NodeBase* last_to_move{ last.m_current_node->m_prev };
1586 std::unique_ptr<NodeBase> fragment{};
1587 std::unique_ptr<NodeBase> fragment_end{ std::move(last_to_move->m_next) };
1588 if (first == other.begin())
1590 fragment = std::move(other.m_head);
1591 other.m_head = std::move(fragment_end);
1592 other.m_head->m_prev =
nullptr;
1596 fragment = std::move(first.m_current_node->m_prev->m_next);
1597 fragment->m_prev->m_next = std::move(fragment_end);
1598 fragment->m_prev->m_next->m_prev = first.m_current_node->m_prev;
1599 fragment->m_prev =
nullptr;
1606 last_to_move->m_next = std::move(m_head);
1607 last_to_move->m_next->m_prev = last_to_move;
1608 m_head = std::move(fragment);
1612 last_to_move->m_next = std::move(temp_prev->m_next);
1613 last_to_move->m_next->m_prev = last_to_move;
1614 temp_prev->m_next = std::move(fragment);
1615 temp_prev->m_next->m_prev = temp_prev;
1619 other.m_size -= dist;
1623 template<
typename T>
1626 transfer(pos, std::move(other), other.
begin(), other.
end());
1629 template<
typename T>
1632 transfer(pos, std::move(other), other.begin(), other.end());
1635 template<
typename T>
1638 transfer(pos, std::move(other), iter, iter.m_current_node->m_next.get());
1641 template<
typename T>
1644 transfer(pos, std::move(other), iter, iter.m_current_node->m_next.get());
1647 template<
typename T>
1651 transfer(pos, std::move(other), first, last);
1654 template<
typename T>
1657 transfer(pos, std::move(other), first, last);
1660 template<
typename T>
1666 while (temp->m_next.get())
1668 next = temp->m_next.get();
1670 if (
Node* node =
dynamic_cast<Node*
>(m_head.get()))
1672 if (node->value() == value)
1675 temp = m_head.get();
1680 if (next && next != m_tail && next->m_next !=
nullptr)
1682 Node* node =
dynamic_cast<Node*
>(next);
1683 if (node->
value() == value)
1690 temp = temp->m_next.get();
1694 template<
typename T>
1697 if (m_head->m_next ==
nullptr)
1702 NodeBase* new_back{ m_head.get() };
1703 std::unique_ptr<NodeBase> sentinel = std::move(m_tail->m_prev->m_next);
1705 std::unique_ptr<NodeBase> temp = std::move(m_head);
1706 std::unique_ptr<NodeBase> prev{};
1708 for (
size_t i = 0; i < m_size; i++)
1710 std::unique_ptr<NodeBase> next = std::move(temp->m_next);
1711 temp->m_next = std::move(prev);
1712 temp->m_prev = next.get();
1714 prev = std::move(temp);
1715 temp = std::move(next);
1718 m_head = std::move(prev);
1719 m_tail->m_prev = new_back;
1720 m_tail->m_prev->m_next = std::move(sentinel);
1723 template<
typename T>
1735 next = prev->m_next.get();
1737 Node* node_next =
dynamic_cast<Node*
>(next);
1738 Node* node_temp =
dynamic_cast<Node*
>(temp);
1739 if (node_next && node_temp)
1741 if (node_next->
value() == node_temp->
value())
1745 if (to_remove->m_next)
1747 to_remove->m_next->m_prev = prev;
1754 prev->m_next = std::move(to_remove->m_next);
1761 prev = prev->m_next.get();
1762 temp = temp->m_next.get();
1767 template<
typename T>
1770 if (index >= m_size)
1778 enum Mode : std::uint8_t { FRONT, BACK, AUTO };
1779 constexpr Mode mode = Mode::AUTO;
1782 if constexpr (mode == FRONT)
1785 temp = m_head.get();
1786 for (
size_t i = 0; i < index; i++)
1788 temp = temp->m_next.get();
1791 else if constexpr (mode == BACK)
1794 temp = m_tail->m_prev;
1795 for (
size_t i = m_size - 1; i > index; i--)
1797 temp = temp->m_prev;
1803 if (index < m_size / 2)
1805 temp = m_head.get();
1806 for (
size_t i = 0; i < index; i++)
1808 temp = temp->m_next.get();
1813 temp = m_tail->m_prev;
1814 for (
size_t i = m_size - 1; i > index; i--)
1816 temp = temp->m_prev;
1821 return dynamic_cast<Node*
>(temp);
1824 template<
typename T>
1830 temp->m_value = value;
1845 template<
typename T>
1850 for (
auto iter = list2.cbegin(); iter != list2.cend(); ++iter)
1867 template<
typename T>
1875 for (
auto iter = list.cbegin(); iter != list.cend(); ++iter)
1878 out << value <<
' ';
1893 template<
typename T>
1896 if (list1.size() != list2.size())
1901 auto list1_iter = list1.cbegin();
1902 auto list2_iter = list2.cbegin();
1904 while (list1_iter != list1.cend())
1906 if (*list1_iter != *list2_iter)
1927 template<
typename T>
1943 template<
typename T>
1946 auto list1_iter = list1.cbegin();
1947 auto list2_iter = list2.cbegin();
1949 while (list1_iter != list1.cend() && list2_iter != list2.cend())
1951 if (*list1_iter > *list2_iter)
1955 if (*list1_iter < *list2_iter)
1966 return list1.size() < list2.size();
1979 template<
typename T>
1995 template<
typename T>
2011 template<
typename T>
constexpr auto operator==(const Array< T, N > &lhs, const Array< T, N > &rhs) noexcept(noexcept(*lhs.begin()== *rhs.begin())) -> bool
The relational operator compares two Array objects.
auto operator<<(std::ostream &out, const Array< T, N > &array) -> std::ostream &
Overloads operator to print all elements of Array.
auto operator*() const -> reference
Overload operator* to dereference Node value / data.
auto operator->() -> pointer
Overload operator-> to get pointer to Node value / data.
auto operator++() -> ListIterator &
Overload pre-increment operator++ to point ListIterator at next Node.
auto operator--() -> ListIterator &
Overload pre-decrement operator-- to point ListIterator at previous Node.
auto operator[](size_t index) -> ListIterator
Overload operator[] for ListIterator iterator access.
std::conditional_t< IF_CONST, const T, T > iterator_type
Alias for iterator type.
ListIterator() noexcept=default
Construct a new ListIterator object.
auto operator--(int) -> ListIterator
Overload post-decrement operator-- to point ListIterator at previous Node.
T value_type
Alias for data type used by iterator.
iterator_type * pointer
Alias for pointer to data type used by iterator.
auto operator++(int) -> ListIterator
Overload post-increment operator++ to point ListIterator at next Node.
std::ptrdiff_t difference_type
Alias for pointer difference type.
iterator_type & reference
Alias for reference to type used by iterator.
auto operator!=(const ListIterator &other) const -> bool
Overload operator!= for ListIterator objects comparison.
std::bidirectional_iterator_tag iterator_category
Alias for bidirectional iterator tag, define iterator direction.
auto operator==(const ListIterator &other) const -> bool
Overload operator!= for ListIterator objects comparison.
auto operator=(NodeBase *node) -> ListIterator &
Overload operator= to assign node to currently pointed ListIterator.
Struct implements base pointer used by List.
NodeBase()=default
Construct a new NodeBase object.
auto operator=(NodeBase &&other) -> NodeBase &=default
Construct NodeBase object using move assignment.
NodeBase(NodeBase &&other)=default
Construct NodeBase object using move constructor.
auto operator=(const NodeBase &other) -> NodeBase &=default
Construct a new NodeBase object using copy assignment.
virtual ~NodeBase()=default
Destroy the Node Base object.
NodeBase(const NodeBase &other)=default
Construct a new NodeBase object using copy constructor.
Implements Node template class with pointer to adjacent elements.
Node(T value)
Construct a new Node object with initial value.
auto value() const -> T
Function returns value stored in Node object.
friend class List
Forward friend declaration of List.
auto value() -> T &
Function returns value stored in Node object.
Implements List using Node with pointer to adjacent elements as internal base.
void unique()
Function removes consecutive duplicated elements.
auto cend() const -> const_iterator
Function returns pointer to List last Node.
~List()
Destroy the List object.
auto front() -> reference
Function returns reference to value stored in List first Node.
auto size() const -> size_t
Function returns List size.
T value_type
Alias for data type used in class.
auto erase(iterator pos) -> iterator
Function erases Node object at specified pos.
void swap(List< T > &other) noexcept
Function swaps content of two List objects.
void remove(const_reference value)
Function removes all elements equal to value.
auto end() -> iterator
Function returns pointer to List last Node.
T * pointer
Alias for pointer to data type used in class.
const T * const_pointer
Alias for const pointer to data type used in class.
auto max_size() const -> size_t
Function returns maximum number of elements container can hold.
auto operator+=(const std::initializer_list< T > init_list) -> List< T > &
push_back elements of another List to base container
auto back() -> reference
Function returns reference to value stored in List last Node.
void clear()
Function removes all elements of List.
auto insert(const const_iterator &pos, const_reference value) -> iterator
Function inserts new Node before specified pos.
List()
Construct a new List object.
ListIterator< true > const_iterator
Alias for const iterator to data type used in class.
T & reference
Alias for reference to data type used in class.
auto get(size_t index) const -> Node *
Function returns pointer to specific Node of List.
void push_front(T value)
Function adds new Node at the beginning of List.
void assign(size_t count, const_reference value)
Function assign values to the List.
void splice(const const_iterator &pos, List< T > &other)
Function moves elements from other List object.
const T & const_reference
Alias for const reference to data type used in class.
auto set(size_t index, T value) -> bool
Function sets value of specifed Node of List.
friend auto operator+(const List< T > &list1, const List< T > &list2) -> List< T >
Construct new object based on two List objects.
void merge(List< T > &other)
Function combines two sorted Lists into one sorted List.
void push_back(T value)
Function adds new Node at the end of List.
void pop_back()
Function removes last Node of List.
auto operator=(const List< T > &other) -> List &
Constructs List using copy assignment.
void reverse()
Function reverts in place Nodes of List.
auto cbegin() const -> const_iterator
Function returns const pointer to List first Node.
void pop_front()
Function removes first Node of List.
ListIterator< false > iterator
Alias for iterator to data type used in class.
auto begin() -> iterator
Function returns pointer to List first Node.
auto empty() const -> bool
Function checks if container has no elements.
void resize(size_t count)
Function resize List to specified number of elements.
auto operator>(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
auto operator>=(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
auto operator<=(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
auto operator+(const List< T > &list1, const List< T > &list2) -> List< T >
Construct new object based on two List objects.
auto operator!=(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
auto operator<(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.