/* * 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 */ #pragma once #include "node.hpp" #include "../debug/assert.hpp" #include #include 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 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 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 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 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" #elif defined(COMPILER_GCC) // gcc#11 If we rely on the _Pragma embedded in `CHECK` then GCC 11 // will still issue a warning for the null assert we're about to do // (but oddly only for some uses of this file...). // // Issuing the _Pragma locally is an effective workaround. // It's unclear how we can produce a repro for this. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnonnull-compare" #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 #elif defined(COMPILER_GCC) #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 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 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 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 bool is_sorted [[gnu::nonnull]] ( NodeT const *head ) { return is_sorted (head, node::value_comparator {std::less<> {}}); }; }