Blame src/listsort.c

Packit e8bc57
#include "config.h"
Packit e8bc57
Packit e8bc57
#include <stdio.h>
Packit e8bc57
Packit e8bc57
/*
Packit e8bc57
 * Demonstration code for sorting a linked list.
Packit e8bc57
 * 
Packit e8bc57
 * The algorithm used is Mergesort, because that works really well
Packit e8bc57
 * on linked lists, without requiring the O(N) extra space it needs
Packit e8bc57
 * when you do it on arrays.
Packit e8bc57
 * 
Packit e8bc57
 * This code can handle singly and doubly linked lists, and
Packit e8bc57
 * circular and linear lists too. For any serious application,
Packit e8bc57
 * you'll probably want to remove the conditionals on `is_circular'
Packit e8bc57
 * and `is_double' to adapt the code to your own purpose. 
Packit e8bc57
 * 
Packit e8bc57
 */
Packit e8bc57
Packit e8bc57
/*
Packit e8bc57
 * This file is copyright 2001 Simon Tatham.
Packit e8bc57
 * 
Packit e8bc57
 * Permission is hereby granted, free of charge, to any person
Packit e8bc57
 * obtaining a copy of this software and associated documentation
Packit e8bc57
 * files (the "Software"), to deal in the Software without
Packit e8bc57
 * restriction, including without limitation the rights to use,
Packit e8bc57
 * copy, modify, merge, publish, distribute, sublicense, and/or
Packit e8bc57
 * sell copies of the Software, and to permit persons to whom the
Packit e8bc57
 * Software is furnished to do so, subject to the following
Packit e8bc57
 * conditions:
Packit e8bc57
 * 
Packit e8bc57
 * The above copyright notice and this permission notice shall be
Packit e8bc57
 * included in all copies or substantial portions of the Software.
Packit e8bc57
 * 
Packit e8bc57
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit e8bc57
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
Packit e8bc57
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit e8bc57
 * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
Packit e8bc57
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Packit e8bc57
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
Packit e8bc57
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Packit e8bc57
 * SOFTWARE.
Packit e8bc57
 */
Packit e8bc57
Packit e8bc57
#include "listsort.h"
Packit e8bc57
typedef unsigned char byte;
Packit e8bc57
#include "word.h"
Packit e8bc57
Packit e8bc57
#ifdef	TEST
Packit e8bc57
Packit e8bc57
static int cmp(const element *a, const element *b);
Packit e8bc57
Packit e8bc57
static int cmp(const element *a, const element *b) {
Packit e8bc57
    return a->i - b->i;
Packit e8bc57
}
Packit e8bc57
#endif
Packit e8bc57
Packit e8bc57
/*
Packit e8bc57
 * This is the actual sort function. Notice that it returns the new
Packit e8bc57
 * head of the list. (It has to, because the head will not
Packit e8bc57
 * generally be the same element after the sort.) So unlike sorting
Packit e8bc57
 * an array, where you can do
Packit e8bc57
 * 
Packit e8bc57
 *     sort(myarray);
Packit e8bc57
 * 
Packit e8bc57
 * you now have to do
Packit e8bc57
 * 
Packit e8bc57
 *     list = listsort(mylist);
Packit e8bc57
 */
Packit e8bc57
Packit e8bc57
element *listsort(element *list, fcn_compare *compare) {
Packit e8bc57
    element *p, *q, *e, *tail;
Packit e8bc57
    int insize, nmerges, psize, qsize, i;
Packit e8bc57
Packit e8bc57
    /*
Packit e8bc57
     * Silly special case: if `list' was passed in as NULL, return
Packit e8bc57
     * NULL immediately.
Packit e8bc57
     */
Packit e8bc57
    if (!list)
Packit e8bc57
	return NULL;
Packit e8bc57
Packit e8bc57
    insize = 1;
Packit e8bc57
Packit e8bc57
    while (1) {
Packit e8bc57
        p = list;
Packit e8bc57
        list = NULL;
Packit e8bc57
        tail = NULL;
Packit e8bc57
Packit e8bc57
        nmerges = 0;  /* count number of merges we do in this pass */
Packit e8bc57
Packit e8bc57
        while (p) {
Packit e8bc57
            nmerges++;  /* there exists a merge to be done */
Packit e8bc57
            /* step `insize' places along from p */
Packit e8bc57
            q = p;
Packit e8bc57
            psize = 0;
Packit e8bc57
            for (i = 0; i < insize; i++) {
Packit e8bc57
                psize++;
Packit e8bc57
		q = q->next;
Packit e8bc57
                if (!q) break;
Packit e8bc57
            }
Packit e8bc57
Packit e8bc57
            /* if q hasn't fallen off end, we have two lists to merge */
Packit e8bc57
            qsize = insize;
Packit e8bc57
Packit e8bc57
            /* now we have two lists; merge them */
Packit e8bc57
            while (psize > 0 || (qsize > 0 && q)) {
Packit e8bc57
Packit e8bc57
                /* decide whether next element of merge comes from p or q */
Packit e8bc57
                if (psize == 0) {
Packit e8bc57
		    /* p is empty; e must come from q. */
Packit e8bc57
		    e = q; q = q->next; qsize--;
Packit e8bc57
		} else if (qsize == 0 || !q) {
Packit e8bc57
		    /* q is empty; e must come from p. */
Packit e8bc57
		    e = p; p = p->next; psize--;
Packit e8bc57
		} else if (compare(p,q) <= 0) {
Packit e8bc57
		    /* First element of p is lower (or same);
Packit e8bc57
		     * e must come from p. */
Packit e8bc57
		    e = p; p = p->next; psize--;
Packit e8bc57
		} else {
Packit e8bc57
		    /* First element of q is lower; e must come from q. */
Packit e8bc57
		    e = q; q = q->next; qsize--;
Packit e8bc57
		}
Packit e8bc57
Packit e8bc57
                /* add the next element to the merged list */
Packit e8bc57
		if (tail) {
Packit e8bc57
		    tail->next = e;
Packit e8bc57
		} else {
Packit e8bc57
		    list = e;
Packit e8bc57
		}
Packit e8bc57
		tail = e;
Packit e8bc57
            }
Packit e8bc57
Packit e8bc57
            /* now p has stepped `insize' places along, and q has too */
Packit e8bc57
            p = q;
Packit e8bc57
        }
Packit e8bc57
	tail->next = NULL;
Packit e8bc57
Packit e8bc57
        /* If we have done only one merge, we're finished. */
Packit e8bc57
        if (nmerges <= 1)   /* allow for nmerges==0, the empty list case */
Packit e8bc57
            return list;
Packit e8bc57
Packit e8bc57
        /* Otherwise repeat, merging lists twice the size */
Packit e8bc57
        insize *= 2;
Packit e8bc57
    }
Packit e8bc57
}
Packit e8bc57
Packit e8bc57
/*
Packit e8bc57
 * Small test rig with three test orders. The list length 13 is
Packit e8bc57
 * chosen because that means some passes will have an extra list at
Packit e8bc57
 * the end and some will not.
Packit e8bc57
 */
Packit e8bc57
Packit e8bc57
#ifdef	TEST
Packit e8bc57
int main(void) {
Packit e8bc57
    #define n 13
Packit e8bc57
    element k[n], *head, *p;
Packit e8bc57
Packit e8bc57
    int order[][n] = {
Packit e8bc57
        { 0,1,2,3,4,5,6,7,8,9,10,11,12 },
Packit e8bc57
        { 6,2,8,4,11,1,12,7,3,9,5,0,10 },
Packit e8bc57
        { 12,11,10,9,8,7,6,5,4,3,2,1,0 },
Packit e8bc57
    };
Packit e8bc57
    unsigned int i, j;
Packit e8bc57
Packit e8bc57
    for (j = 0; j < n; j++)
Packit e8bc57
        k[j].i = j;
Packit e8bc57
Packit e8bc57
    listsort(NULL, cmp, 0, 0);
Packit e8bc57
Packit e8bc57
    for (i = 0; i < sizeof(order)/sizeof(*order); i++) {
Packit e8bc57
	int *ord = order[i];
Packit e8bc57
	head = &k[ord[0]];
Packit e8bc57
	for (j = 0; j < n; j++) {
Packit e8bc57
	    if (j == n-1)
Packit e8bc57
		k[ord[j]].next = NULL;
Packit e8bc57
	    else
Packit e8bc57
		k[ord[j]].next = &k[ord[j+1]];
Packit e8bc57
	}
Packit e8bc57
Packit e8bc57
	printf("before:");
Packit e8bc57
	p = head;
Packit e8bc57
	do {
Packit e8bc57
	    printf(" %d", p->i);
Packit e8bc57
	    p = p->next;
Packit e8bc57
	} while (p != NULL);
Packit e8bc57
	printf("\t");
Packit e8bc57
	head = listsort(head, cmp);
Packit e8bc57
	printf(" after:");
Packit e8bc57
	p = head;
Packit e8bc57
	do {
Packit e8bc57
	    printf(" %d", p->i);
Packit e8bc57
	    p = p->next;
Packit e8bc57
	} while (p != NULL);
Packit e8bc57
	printf("\n");
Packit e8bc57
    }
Packit e8bc57
    printf("\n");
Packit e8bc57
Packit e8bc57
    return 0;
Packit e8bc57
}
Packit e8bc57
#endif