/* An efficient linked list sort function */
/* Philip J. Erdelsky - September 6, 1988 */

#include <stdio.h>

struct link {
  struct link *next;
  /* other members not directly accessed by llsort() */
  };

struct link *llsort(p, compare) struct link *p; int (*compare)(); {
int base;
unsigned int block;

struct tape {
  struct link first, *last;
  unsigned int count;
  } tape[4];

tape[0].count = 0; tape[0].last = &tape[0].first;
tape[1].count = 0; tape[1].last = &tape[1].first;

for (base=0; p!=NULL; p=p->next, base^=1) {
  tape[base].last = tape[base].last->next = p;
  tape[base].count++;
  }

for (base=0, block=1; tape[base+1].count!=0; base^=2, block<<=1) {
  int dest;
  struct tape *tape0, *tape1;
  tape0 = tape + base;
  tape1 = tape + base + 1;
  dest = base^2;
  tape[dest].count = 0; tape[dest].last = &tape[dest].first;
  tape[dest+1].count = 0; tape[dest+1].last = &tape[dest+1].first;
  for (; tape0->count!=0; dest^=1) {
    unsigned int n0, n1;
    struct tape *output_tape;
    output_tape = tape + dest;
    n0 = n1 = block;
    while (1) {
      struct link *chosen_item;
      struct tape *chosen_tape;
      if (n0==0 || tape0->count==0) {
        if (n1==0 || tape1->count==0) break;
        chosen_tape = tape1;
        n1--;
        }
      else if (n1==0 || tape1->count==0) {
        chosen_tape = tape0;
        n0--;
        }
      else if ((*compare)(tape0->first.next,tape1->first.next)>0) {
        chosen_tape = tape1;
        n1--;
        }
      else {
        chosen_tape = tape0;
        n0--;
        }
      chosen_tape->count--;
      chosen_item = chosen_tape->first.next;
      chosen_tape->first.next = chosen_item->next;
      output_tape->last = output_tape->last->next = chosen_item;
      output_tape->count++;
      }
    }
  }

tape[base].last->next = NULL;
return tape[base].first.next;
}
