/* * 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 "../debug/assert.hpp" #include #include namespace cruft::list { /// A simple tuple-style linked list node. template struct node { node *next; ValueT data; }; /// 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 node* midpoint [[gnu::nonnull]] ( node *first, node *last ) { // Pre-increment the fast node to trigger a preference for the earlier // of the middle nodes. auto slow = first; auto fast = first->next; while (fast != last->next) { fast = fast->next; if (fast == last->next) return slow; fast = fast->next; slow = slow->next; } 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 node* merge [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( node *first, node *middle, node *last ) { // 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] () { auto &target = first->data < middle->data ? first : middle; auto res = target; target = target->next; return res; }; node *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 != last->next) { auto target = extract (); cursor->next = target; cursor = target; } // Append the remaining data to the new list. if (first == middle || !first) cursor->next = middle; else if (middle == last->next || !middle) cursor->next = first; #if defined(COMPILER_CLANG) #pragma GCC diagnostic pop #endif return head; } /// An in-place merge sort for singly linked lists. template node* sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( node *first, node *last ) { if (first == last) return first; auto a_end = ::cruft::list::midpoint (first, last); auto middle = a_end->next; a_end->next = nullptr; auto a = ::cruft::list::sort (first, a_end); auto b = ::cruft::list::sort (middle, last); return ::cruft::list::merge (a, b, last); } /// Tests if a singly linked list is sorted. template bool is_sorted [[gnu::nonnull]] ( node const *head ) { if (!head->next) return true; auto a = head; auto b = head->next; while (b) { if (b->data < a->data) return false; a = a->next; b = b->next; } return true; } }