231 lines
6.1 KiB
C++
231 lines
6.1 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "node.hpp"
|
|
|
|
#include "../debug/assert.hpp"
|
|
|
|
#include <iterator>
|
|
#include <utility>
|
|
|
|
namespace cruft::list {
|
|
/// Return the total number of nodes in the list
|
|
///
|
|
/// It is safe to call this function with a null node pointer.
|
|
template <typename NodeT>
|
|
std::size_t
|
|
size (NodeT const *head)
|
|
{
|
|
std::size_t count = 0;
|
|
|
|
while (head) {
|
|
++count;
|
|
head = next (head);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
/// Returns the last valid node of a linked list. ie, the node where the
|
|
/// successor is null.
|
|
///
|
|
/// The results are undefined if the head node is null (as it is
|
|
/// impossible to return a valid node if none are valid).
|
|
template <typename NodeT>
|
|
NodeT*
|
|
end (NodeT *head)
|
|
{
|
|
CHECK (head);
|
|
|
|
while (next (head))
|
|
head = next (head);
|
|
return head;
|
|
}
|
|
|
|
|
|
/// Find the midpoint of the list specified by the sub-list [first, last].
|
|
///
|
|
/// On even length lists the earlier middle node will be returned.
|
|
///
|
|
/// Uses the fast/slow walking method. Double advancing the fast iterator,
|
|
/// and singly advancing the slow iterator.
|
|
///
|
|
/// This necessarily walks the entire list, so the performance may be
|
|
/// quite low.
|
|
///
|
|
/// NOTE: `last` is a valid node and forms part of the sub-list.
|
|
template <typename NodeT>
|
|
NodeT*
|
|
midpoint [[gnu::nonnull]] (
|
|
NodeT *first,
|
|
NodeT *last
|
|
) {
|
|
// Pre-increment the fast node to trigger a preference for the earlier
|
|
// of the middle nodes.
|
|
auto slow = first;
|
|
auto fast = next (first);
|
|
|
|
while (fast != next (last)) {
|
|
fast = next (fast);
|
|
if (fast == next (last))
|
|
return slow;
|
|
|
|
fast = next (fast);
|
|
slow = next (slow);
|
|
}
|
|
|
|
return slow;
|
|
}
|
|
|
|
/// Merges a list that has been divided into two sorted halves.
|
|
///
|
|
/// This operation will mutate the `next` pointers but will not mutate
|
|
/// the inner data objects.
|
|
///
|
|
/// NOTE: The list is specified inclusively, and the `last` node is a
|
|
/// valid piece of data.
|
|
///
|
|
/// Returns the first node of the newly sorted list. The caller _must_
|
|
/// store this value otherwise a portion of the list will be lost.
|
|
template <typename NodeT, typename ComparatorT>
|
|
NodeT*
|
|
merge [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
|
|
NodeT *first,
|
|
NodeT *middle,
|
|
NodeT *last,
|
|
ComparatorT &&cmp
|
|
) {
|
|
// clang#xxxx clang mistakenly warns that the values of 'first' and
|
|
// 'middle' will be unconditionally true on the first iteration of the
|
|
// while loop.
|
|
#if defined(COMPILER_CLANG)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpointer-bool-conversion"
|
|
#endif
|
|
|
|
CHECK (first);
|
|
CHECK (middle);
|
|
CHECK (last);
|
|
|
|
if (first == last)
|
|
return first;
|
|
|
|
// Return a reference to the lower of 'first' and 'middle', and
|
|
// advance the cursor.
|
|
auto extract = [&first, &middle, &cmp] () {
|
|
auto &target = cmp (first, middle) ? first : middle;
|
|
auto res = target;
|
|
target = next (target);
|
|
return res;
|
|
};
|
|
|
|
NodeT *cursor = extract ();
|
|
auto head = cursor;
|
|
|
|
// Keep adding the lowest node to the new list until we exhaust one
|
|
// or both of our sources.
|
|
while (first && middle && first != middle && middle != next (last)) {
|
|
auto target = extract ();
|
|
next (cursor, target);
|
|
cursor = target;
|
|
}
|
|
|
|
// Append the remaining data to the new list.
|
|
if (first == middle || !first)
|
|
next (cursor, middle);
|
|
else if (middle == next (last) || !middle)
|
|
next (cursor, first);
|
|
|
|
#if defined(COMPILER_CLANG)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
return head;
|
|
}
|
|
|
|
|
|
/// Sort a linked list using a supplied comparator.
|
|
///
|
|
/// The current implementation uses an in-place merge sort.
|
|
///
|
|
/// Each nodes successor may be modified, but the data will not be.
|
|
///
|
|
/// Returns the head of the newly sorted list. The caller _must_ reset
|
|
/// their stored head node with this value otherwise data may be lost.
|
|
template <typename NodeT, typename ComparatorT>
|
|
NodeT*
|
|
sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
|
|
NodeT *first,
|
|
NodeT *last,
|
|
ComparatorT &&cmp
|
|
) {
|
|
if (first == last)
|
|
return first;
|
|
|
|
auto a_end = ::cruft::list::midpoint (first, last);
|
|
auto middle = next (a_end);
|
|
next (a_end, nullptr);
|
|
|
|
auto a = ::cruft::list::sort (first, a_end, cmp);
|
|
auto b = ::cruft::list::sort (middle, last, cmp);
|
|
return ::cruft::list::merge (a, b, last, cmp);
|
|
}
|
|
|
|
|
|
/// Sort a linked list using the default (value) comparator.
|
|
template <typename NodeT>
|
|
NodeT*
|
|
sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
|
|
NodeT *head,
|
|
NodeT *tail
|
|
) {
|
|
return sort (
|
|
head,
|
|
tail,
|
|
node::value_comparator (std::less<> ())
|
|
);
|
|
}
|
|
|
|
|
|
/// Tests if a singly linked list is sorted.
|
|
template <typename NodeT, typename ComparatorT>
|
|
bool
|
|
is_sorted [[gnu::nonnull]] (
|
|
NodeT const *head,
|
|
ComparatorT &&cmp
|
|
) {
|
|
if (!next (head))
|
|
return true;
|
|
|
|
auto a = head;
|
|
auto b = next (head);
|
|
|
|
while (b) {
|
|
if (cmp (b, a))
|
|
return false;
|
|
|
|
a = b;
|
|
b = next (b);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
template <typename NodeT>
|
|
bool
|
|
is_sorted [[gnu::nonnull]] (
|
|
NodeT const *head
|
|
) {
|
|
return is_sorted (head, node::value_comparator {std::less<> {}});
|
|
};
|
|
}
|