/*
 *  slist.h - simple intrusive cyclic single linked list
 *
 *  Copyright (C)         Lumiera.org
 *    2009                Anton Yakovlev <just.yakovlev@gmail.com>
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef SLIST_H
#define SLIST_H

#include <stddef.h>

/**
 * @file
 * Intrusive cyclic single linked list.
 *
 * List node is a structure, which consists only of a forward pointer. This is
 * much easier and makes code much cleaner, than to have forward pointer as is.
 * In a empty initialised node, this pointer points to the node itself. Note
 * that this pointer can never ever become NULL.
 *
 * This lists are used by using one node as 'root' node where it's pointer is
 * the head pointer to the actual list. Care needs to be taken to ensure not to
 * apply any operations meant to be applied to data nodes to the root node.
 * This way is the preferred way to use this lists.
 *
 * Alternatively one can store only a chain of data nodes and use a SList
 * pointer to point to the first item (which might be NULL in case no data is
 * stored). When using such approach care must be taken since most functions
 * below expect lists to have a root node.
 *
 * Due to nature of single linked list, there's no easy way to implement
 * functions, which need reverse passing through a list. But some of L1-list
 * interface functions need such ability (for example, when we need to find
 * previous element for current element). Because search of previous element
 * requires visiting of exactly N-1 nodes (where N is length of L1-list), we
 * use root node as start point. This gives to us probability of visiting
 * 1 <= C <= N-1 nodes, and, thus, speed up search.
 *
 * This header can be used in 2 different ways:
 * 1) (preferred) just including it provides all functions as static inlined
 *    functions. This is the default
 * 2) #define LLIST_INTERFACE before including this header gives only the declarations
 *    #define LLIST_IMPLEMENTATION before including this header yields in definitions
 *    this can be used to generate a library. This is currently untested and not
 *    recommended.
 * The rationale for using inlined functions is that most functions are very
 * small and likely to be used in performance critical parts. Inlining can give
 * a huge performance and optimisation improvement here. The few functions
 * which are slightly larger are expected to be the less common used ones, so
 * inlining them too shouldn't be a problem either.
 */

/* TODO __STDC_VERSION__ 199901L
150) This macro was not specified in ISO/IEC 9899:1990 and was specified as 199409L in
ISO/IEC 9899/AMD1:1995. The intention is that this will remain an integer constant of type long
int that is increased with each revision of this International Standard.
*/

#ifdef HAVE_INLINE
#   define SLIST_MACRO static inline
#else
#   ifdef __GNUC__
#       define SLIST_MACRO static __inline__
#   else
#       define SLIST_MACRO static
#   endif
#endif

#if defined(SLIST_INTERFACE)
/* only the interface is generated */
#define SLIST_FUNC(proto, ...) proto
#elif defined(SLIST_IMPLEMENTATION)
/* generate a non inlined implementation */
#define SLIST_FUNC(proto, ...) proto { __VA_ARGS__ }
#else
/* all functions are macro-like inlined */
#define SLIST_FUNC(proto, ...) SLIST_MACRO proto { __VA_ARGS__ }
#endif

/*
 * Type of a slist node.
 */

#ifndef SLIST_DEFINED
#define SLIST_DEFINED

struct slist_struct {
    struct slist_struct* next;
};

#endif

typedef struct slist_struct slist;
typedef slist* SList;
typedef const slist* const_SList;
typedef slist** SList_ref;

/**
 * Macro to instantiate a local llist.
 *
 * @param name of the slist node
 */

#define SLIST_AUTO( name ) slist name = { &name }

/*
 * some macros for convenience
 */

#define slist_head slist_next
#define slist_insert_head( list, element ) slist_insert( list, element )

/**
 * Сast back from a member of a structure to a pointer of the structure.
 *
 * Example:
 *
 *  struct point {
 *      int x;
 *      int y;
 *      slist list;
 *  };
 *
 *  SList points = ...; // some initialization; must be the root of our list
 *
 *  SLIST_FOREACH( points, current_node ) {
 *      struct point* current_point = SLIST_TO_STRUCTP( current_node, struct point, list );
 *      printf( "point = ( %d, %d )\n", current_point -> x, current_point -> y );
 *  }
 *
 * @param list is a pointer to the SList member of the linked structures
 * @param type is type name of the linked structures
 * @param member is a name of the SList member of the linked structures
 */

#define SLIST_TO_STRUCTP( list, type, member ) \
    ( ( type* ) ( ( ( char* )( list ) ) - offsetof( type, member ) ) )

/**
 * Iterate forward over a list.
 *
 * @param list the root node of the list to be iterated
 * @param node pointer to the iterated node
 */

#define SLIST_FOREACH( list, node ) \
    for ( SList node = slist_head( list ); ! slist_is_end( node, list ); slist_forward( &node ) )

/**
 * Iterate forward over a range.
 *
 * @param start first node to be interated
 * @param end node after the last node be iterated
 * @param node pointer to the iterated node
 */

#define SLIST_FORRANGE( start, end, node ) \
    for ( SList node = start; node != end; slist_forward( &node ) )

/**
 * Consume a list from head.
 * The body of this statement should remove the head from the list, else it would be a infinite loop
 *
 * @param list the root node of the list to be consumed
 * @param head pointer to the head node
 */

#define SLIST_WHILE_HEAD( list, head ) \
    for ( SList head = slist_head( list ); ! slist_is_empty( list ); head = slist_head( list ) )

/**
 * Initialise a new llist.
 * Must not be applied to a list node which is not empty! Lists need to be initialised
 * before any other operation on them is called.
 *
 * @param list node to be initialised
 */

SLIST_FUNC (
    void slist_init( SList list ),
    list -> next = list;
);

/**
 * Check if a node is not linked with some other node.
 */

SLIST_FUNC (
    int slist_is_empty( const_SList list ),
    return list -> next == list;
);

/**
 * Check if self is the only node in a list or self is not in a list.
 *
 * Warning:
 *      There's no check for empty list, so if you have a list with no items,
 *      you'll get seg fault here.
 *
 * @param list is root node of the list to be checked
 */

SLIST_FUNC (
    int slist_is_single( const_SList list ),
    return list -> next -> next == list;
);

/**
 * Check for the head of a list.
 *
 * @param list is root node of the list
 * @param head is expected head of the list
 */

SLIST_FUNC (
    int slist_is_head( const_SList list, const_SList head ),
    return list -> next == head;
);

/**
 * Check for the end of a list.
 * The end is by definition one past the tail of a list, which is the root node itself.
 *
 * @param list is root node of the list
 * @param end is expected end of the list
 */

SLIST_FUNC (
    int slist_is_end( const_SList list, const_SList end ),
    return list == end;
);

/**
 * Check if a node is a member of a list.
 *
 * @param list is root node of the list
 * @param member is node to be searched
 */

SLIST_FUNC (
    int slist_is_member( const_SList list, const_SList member ),
    for ( const_SList i = member -> next; i != member; i = i -> next ) {
        if ( i == list ) {
            return 1;
        }
    }
    return 0;
);

/**
 * Check the order of elements in a list.
 *
 * @param list is root node of the list
 * @param before is expected to be before after
 * @param after is expected to be after before
 */

SLIST_FUNC (
    int slist_is_before_after( const_SList list, const_SList before, const_SList after ),
    for ( const_SList i = before -> next; i != list; i = i -> next ) {
        if ( i == after ) {
            return 1;
        }
    }
    return 0;
);

/**
 * Count the nodes of a list.
 *
 * @param list is root node of the list
 * @return number of nodes in `list`
 */

SLIST_FUNC (
    unsigned slist_count( const_SList list ),
    unsigned cnt = 0;
    for ( const_SList i = list; i -> next != list; ++cnt, i = i -> next ) {
        ;
    }
    return cnt;
);

/**
 * Get next node.
 * Will not stop at tail.
 *
 * @param node is current node
 * @return node after current node
 */

SLIST_FUNC (
    SList slist_next( const_SList node ),
    return node -> next;
);

/**
 * Get previous node.
 *
 * @param list is root node of the list
 * @param node is current node
 * @return node before current node
 */

SLIST_FUNC (
    SList slist_prev( SList list, SList node ),
    while ( list -> next != node ) {
        list = list -> next;
    }
    return list;
);

/**
 * Remove a node from a list.
 *
 * @param list is root node of the list
 * @param node to be removed
 * @return node
 */

SLIST_FUNC (
    SList slist_unlink( SList list, SList node ),
    SList prev_node = slist_prev( list, node );
    prev_node -> next = node -> next;
    return node -> next = node;
);

/**
 * Insert a node after another.
 *
 * @param head is node after which we want to insert
 * @param node is node which shall be inserted after `head`. Could already linked to a list from where it will be removed.
 * @return head
 */

SLIST_FUNC (
    SList slist_insert( SList head, SList node ),
    if ( ! slist_is_empty( node ) ) {
        slist_unlink( node, node );
    }
    node -> next = head -> next;
    head -> next = node;
    return head;
);

/**
 * Move the content of a list after a node in another list.
 *
 * @param xnode is node after which we want to insert a list
 * @param ylist is root node of the list which shall be inserted after self. This list will be empty after call.
 * @return xnode
 */

SLIST_FUNC (
    SList slist_insert_list( SList xnode, SList ylist ),
    if ( ! slist_is_empty( ylist ) ) {
        SList tail = slist_prev( ylist, ylist ); // search for the Y list tail
        tail -> next = xnode -> next;
        xnode -> next = ylist -> next;

        ylist -> next = ylist; // clear the Y list
    }
    return xnode;
);

/**
 * Move a range of nodes after a given node.
 *
 * @param node is node after which the range shall be inserted
 * @param start first node in range to be moved
 * @param end node after the last node of the range
 * @return node
 */

SLIST_FUNC (
    SList slist_insert_range( SList node, SList start, SList end ),
    // insert range
    SList tail = slist_prev( start, end ); // search for the end of range
    tail -> next = node -> next;
    node -> next = start -> next;
    // fix list
    start -> next = end;
    return node;
);

/**
 * Swap a node with its next node.
 * Advancing will not stop at tail, one has to check that if this is intended.
 *
 * @param list is root node of the list
 * @param node is node to be advaced
 * @return node
 */

SLIST_FUNC (
    SList slist_advance( SList list, SList node ),
    SList prev = slist_prev( list, node );
    prev -> next = node -> next;
    node -> next = node -> next -> next;
    prev -> next -> next = node;
    return node;
);

/**
 * Advance a pointer to a node to its next node.
 *
 * @param node pointer-to-pointer to the current node. `node` will point to the next node after this call.
 */

SLIST_FUNC (
    void slist_forward( SList_ref node ),
    *node = ( *node ) -> next;
);

/**
 * Get the nth element of a list (this function does not stop at head/tail).
 *
 * @param list is root node of the list to be queried
 * @param n is number of element to find
 * @return |n|-th element of list
 */

SLIST_FUNC (
    SList slist_get_nth( SList list, unsigned int n ),
    while ( n-- > 0 ) {
        list = slist_next( list );
    }
    return list;
);

/**
 * Get the nth element of a list with a stop node.
 *
 * @param list is root node of the list to be queried
 * @param n is number of element to find
 * @param stop is node which will abort the iteration
 * @return |n|-th element of list or NULL if `stop` node has been reached
 */

SLIST_FUNC (
    SList slist_get_nth_stop( SList list, unsigned int n, const_SList stop ),
    while ( n-- > 0 ) {
        list = slist_next( list );
        if ( list == stop ) {
            return NULL;
        }
    }
    return list;
);

/**
 * Sort a list.
 *
 * This is iterative version of bottom-up merge sort for (L1/L2) linked-list:
 *  + there's no recursion
 *  + there's no extra stackspace allocation (only a few bytes for locals)
 * Such implementation should be optimal and fast enough.
 *
 * Maybe this function is too big for inlining (though I don't think so), so
 * maybe somebody can make it smaller without losing perfomance? ;)
 *
 * @param list is root node of a list to be sorted
 * @param cmp is compare function of 2 SList items
 * @return list
 */

typedef int ( *slist_cmpfn )( const_SList a, const_SList b );

SLIST_FUNC (
    SList slist_sort( SList list, slist_cmpfn cmp ),
    if ( ! slist_is_single( list ) ) {

        unsigned int length = slist_count( list );

        // `max_size` is a half of minimum power of 2, greater of equal to `length`
        // ( 2 * max_size = 2^k ) >= length
        // We need `max_size` value for proper binary division of a list for sorting.
        
        unsigned long long max_size = 1;
        while ( ( max_size << 1 ) < length ) {
            max_size <<= 1;
        }

        // The main idea of bottom-up merge sort is sequential merging of each pair
        // of sequences of { 1, .. 2^k, .. max_size } length. That's all. :)

        for ( unsigned int size = 1; size <= max_size; size <<= 1 ) {

            // On each iteration:
            //  * `result` points to the current node of global (merged/sorted) list.
            //    thus, we can holds all nodes are linked.
            //  * `left` and `right` points to begin of (sub)lists for merging.
            
            SList result = list;
            SList left = list -> next;
            SList right;

            // Process each pairs of sequences of size=2^k length.
            
            for ( unsigned int position = 0; position < length; position += size + size ) {

                right = slist_get_nth_stop( left, size, list );

                unsigned int size_left = size;
                unsigned int size_right = right == NULL ? 0 : size;

                // Here we have 2 sublists of `size_left` and `size_right` sizes.
                // Implementation of `merge` function is next three loops.

                while ( ( size_left > 0 ) && ( size_right > 0 ) ) {

                    if ( cmp( left, right ) <= 0 ) {
                        
                        result -> next = left;
                        
                        left = left -> next;
                        if ( left == list ) {
                            size_left = 0;
                        } else {
                            size_left--;
                        }

                    } else {
                        
                        result -> next = right;
                        
                        right = right -> next;
                        if ( right == list ) {
                            size_right = 0;
                        } else {
                            size_right--;
                        }
                        
                    }
                    
                    result = result -> next;
                    
                }

                while ( size_left > 0 ) {
                    result -> next = left;
                    result = left;
                    
                    left = left -> next;
                    if ( left == list ) {
                        break;
                    }
                    size_left--;
                }

                while ( size_right > 0 ) {
                    result -> next = right;
                    result = right;
                    
                    right = right -> next;
                    if ( right == list ) {
                        break;
                    }
                    size_right--;
                }

                // go to begin of next pair of sequences
                
                left = right;
                
            }

            // here `result` points to the last node of a list.
            // we wanna keep cyclic list.
            
            result -> next = list;
            
        }

    }
    return list;
)
            
/**
 * Find the first occurence of an element in a list.
 * Does not change the order of a list.
 *
 * @param list is root node of a list to be searched
 * @param pattern is template for the element being searched
 * @param cmp is compare function of 2 SList items
 * @return pointer to the found SList element or NULL if nothing found
 */

SLIST_FUNC (
    SList slist_find( const_SList list, const_SList pattern, slist_cmpfn cmp ),
    SLIST_FOREACH( list, node ) {
        if ( cmp( node, pattern ) == 0 ) {
            return node;
        }
    }
    return NULL;
)

/**
 * Find the first occurence of an element in an unsorted list.
 *
 * Searches the list until it finds the searched element and moves it then to
 * the head. Useful if the order of the list is not required and few elements
 * are frequently searched.
 *
 * @param list is root node of a list to be searched
 * @param pattern is template for the element being searched
 * @param cmp is compare function of 2 SList items
 * @return pointer to the found SList element (head) or NULL if nothing found
 */

SLIST_FUNC (
    SList slist_ufind( SList list, const_SList pattern, slist_cmpfn cmp ),
    SLIST_FOREACH( list, node ) {
        if ( cmp( node, pattern ) == 0 ) {
            slist_insert_head( list, node );
            return node;
        }
    }
    return NULL;
)

/**
 * Find the first occurence of an element in a sorted list.
 *
 * Searches the list until it finds the searched element, exits searching when
 * found an element biggier than the searched one.
 *
 * @param list is root node of a list to be searched
 * @param pattern is template for the element being searched
 * @param cmp is compare function of 2 SList items
 * @return pointer to the found SList element (head) or NULL if nothing found
 */

SLIST_FUNC (
    SList slist_sfind( const_SList list, const_SList pattern, slist_cmpfn cmp ),
    SLIST_FOREACH( list, node ) {
        
        int result = cmp( node, pattern );
        
        if ( result == 0 ) {
            return node;
        } else if ( result > 0 ) {
            break;
        }
        
    }
    return NULL;
)

#endif /* SLIST_H */
