
/* Copyright (C) 1992  AHPCRC, Univeristy of Minnesota
 *
 * 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 in a file named 'Copying'; if not, write to
 * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139.
 */

/* Author:
 *	Ken Chin-Purcell (ken@ahpcrc.umn.edu)
 *	Army High Performance Computing Research Center (AHPCRC)
 *	Univeristy of Minnesota
 *
 * $Header: /usr/people/ken/gvl/lib/RCS/skip.c,v 2.2 92/10/19 16:55:42 ken Exp $
 *
 * $Log:	skip.c,v $
 * Revision 2.2  92/10/19  16:55:42  ken
 * Trivial change
 * 
 * Revision 1.1  92/10/19  16:37:09  ken
 * Initial revision
 * 
 */

#include <math.h>
#include <stdio.h>
#include <malloc.h>

#include "skip.h"
#include "util.h"

extern void	exit(int);


/* Note that node->forward[0] is already allocated in SLNode.
 * MaxLevel = 15 implies 16 total levels.
 */

#define MaxLevel	15

#define RANDOM_MAX	2147483647		/* 2**31 - 1 */
#define P_E		 790015084		/* p value of 1/e */
#define P_500		(RANDOM_MAX / 2)	/* p value of .500 */
#define P_250		(RANDOM_MAX / 4)	/* p value of .250 */

#define NewNode(x) (SLNode *) malloc(sizeof(SLNode) + (x)*sizeof(SLNode *))


SkipList SkipNew(int (*compare)(void *, void *), int unique)
{
    /* Create a new skip list, with compare as the compare function
     * If unique is non-zero duplicate items will be rejected
     * when inserted into the list.
     */
    SkipList	l;
    int		i;
    
    Verify(compare, "Need a compare function for skip list");
    
    l = MallocType(struct _SkipListRec);
    MemCheck(l);
    
    l->last     = NULL;
    l->level    = 0;
    l->compare	= compare;
    l->unique	= unique;
    l->size     = 0;
    
    l->header = NewNode(MaxLevel);
    MemCheck(l->header);
    
    for (i = 0; i <= MaxLevel; i++)
	l->header->forward[i] = NULL;
    
    return l;
}


void SkipFreeData(SkipList l, void (*freeitem)(void *))
{
    /* Free keys.
     */
    SLNode	*q, *p = l->header->forward[0];
    int		i;
    
    while (p) {
	q = p->forward[0];
	if (freeitem)
	    freeitem(p->key);
	free(p);
	p = q;
    }

    l->last     = NULL;
    l->level    = 0;
    l->size     = 0;
    
    for (i = 0; i <= MaxLevel; i++)
	l->header->forward[i] = NULL;
}


void SkipFree(SkipList l, void (*freeitem)(void *))
{
    /* Free the list, header node, and keys.
     */
    SkipFreeData(l, freeitem);
    free(l->header);
    free(l);
}


static SLNode *KeySearch(SkipList l, void *key, SLNode **update)
{
    /* The core search routine for skiplist lookups.
     * The update vector is used in inserts and deletes.
     * This routine should execute in O(log(n)).
     */
    int		i;
    SLNode	*q = NULL, *p = l->header;

    for (i = l->level; i >= 0; i--) {
	while ((q = p->forward[i])  &&  l->compare(q->key, key) < 0)
	    p = q;
	if (update)
	    update[i] = p;
    }
    
    return q;
}


int SkipInsert(SkipList l, void *key)
{
    /* Insert a key into the list.  q is the node before the key
     * value, update the vector of nodes before the key value.
     *
     * Return 1 on success, 0 on failure (duplicate).
     */
    int		i;
    SLNode	*update[MaxLevel+1];
    SLNode	*q = KeySearch(l, key, update);

    /* First test for a duplicate item.
     */
    if (l->unique && q && l->compare(q->key, key) == 0)
	return 0;
    
    /* Pick a level
     */
    i = 0;
    while (random() < P_250  &&  i < MaxLevel)
	i++;
    if (i > l->level) {
	i = ++l->level;
	update[i] = l->header;
    }
    
    /* Create a new node at that level.
     */
    q = NewNode(i);
    MemCheck(q);
    q->key = key;

    /* Patch up skip list.
     */
    for (; i >= 0; i--)	{
	q->forward[i] = update[i]->forward[i];
	update[i]->forward[i] = q;
    }
    l->size++;
    l->last = q;
    
    return 1;
}


void *SkipRemove(SkipList l, void *key)
{
    /* Delete key from the skip list.
     * Like ListInsert, q is the item in question,
     * update is the vector of nodes pointing to q.
     *
     * Return value on success, NULL on failure (not found).
     */
    int		i;
    SLNode	*update[MaxLevel+1];
    SLNode	*q = KeySearch(l, key, update);
    void	*value;
    
    if (q && l->compare(q->key, key) == 0) {

	for (i = 0; i <= l->level; i++) {
	    if (update[i]->forward[i] != q)
		break;
	    update[i]->forward[i] = q->forward[i];
	}
	l->size--;
	l->last = update[0];
	
	value = q->key;
	free(q);
	
	while(!l->header->forward[l->level]  &&  l->level > 0)
	    l->level--;

	return value;
    }

    return NULL;
}


void *SkipSearch(SkipList l, void *key)
{
    /* Return key value, or NULL if not found.
     */
    SLNode	*q = KeySearch(l, key, NULL);
    
    l->last = q;
    if (q && l->compare(q->key, key) == 0)
	return q->key;
    return NULL;
}


void *SkipHead(SkipList l)
{
    l->last = l->header->forward[0];
    return l->last ? l->last->key : NULL;
}


void *SkipNext(SkipList l)
{
    if (l->last)
	l->last = l->last->forward[0];
    return l->last ? l->last->key : NULL;
}


int SkipSize(SkipList l)
{
    return l->size;
}


/* We can use the position of the item in the list as the key, to
 * create a stack, or ordered list.  See "A Skip List Cookbook"
 * by Pugh for details.
 *
 * The stack numbering is based at one, the header node is index 0.  
 *
 * Instead of the position, the offset to the next element is
 * stored in the node.  Note that p->forward[0].distance == 1
 * always.
 *
 * Note also the macros StackPush, StackPop and StackAppend in skip.h
 */

#define NewStackNode(x) (StackNode *) \
    malloc(sizeof(StackNode) + (x)*sizeof(StackRef))

SkipStack StackNew(void)
{
    /* Create a variable size ordered list 
     */
    SkipStack	l;
    
    l = MallocType(struct _SkipStackRec);
    MemCheck(l);
    
    l->last  = NULL;
    l->level = 0;
    l->size  = 0;
    
    l->header = NewStackNode(MaxLevel);
    MemCheck(l->header);
    
    l->header->forward[0].node = NULL;
    l->header->forward[0].distance = 1;
    
    return l;
}


void StackFreeData(SkipStack l, void (*freeitem)(void *))
{
    /* Free values if freeitem defined.
     */
    StackNode	*p = l->header->forward[0].node, *q;
    
    while (p) {
	q = p->forward[0].node;
	if (freeitem)
	    freeitem(p->value);
	free(p);
	p = q;
    }

    l->last  = NULL;
    l->level = 0;
    l->size  = 0;
    
    l->header->forward[0].node = NULL;
    l->header->forward[0].distance = 1;
}


void StackFree(SkipStack l, void (*freeitem)(void *))
{
    /* Free nodes, values if freeitem defined, header, and stack.
     */
    StackFreeData(l, freeitem);
    free(l->header);
    free(l);
}


void *StackSearch(SkipStack l, int k)
{
    /* Get the k'th element in list.
     * Return NULL if k is out of range.
     */
    int		i, pos = 0;
    StackNode	*p = l->header;
    
    if (k < 1  ||  k > l->size)
	return NULL;

    for (i = l->level; i >= 0; i--)
	while (pos + p->forward[i].distance <= k) {
	    pos += p->forward[i].distance;
	    p = p->forward[i].node;
	}
    l->last = p;

    return p->value;
}


void StackAppend(SkipStack l, int k, void *value)
{
    /* Append value after the k'th element
     */
    int		i, lvl, pos = 0;
    StackNode	*p = l->header, *q;

    if (k > l->size)
	k = l->size;
    if (k < 0)
	k = 0;
    
    lvl = 0;
    while (random() < P_250  &&  lvl < MaxLevel)
	lvl++;
    if (lvl > l->level) {
	lvl = ++l->level;
	l->header->forward[lvl].node = NULL;
	l->header->forward[lvl].distance = l->size + 1;
    }
    
    q = NewStackNode(lvl);
    MemCheck(q);
    q->value = value;
    
    for (i = l->level; i >= 0; i--) {
	while (pos + p->forward[i].distance <= k) {
	    pos += p->forward[i].distance;
	    p = p->forward[i].node;
	}
	if (i > lvl)
	    p->forward[i].distance++;
	else {
	    q->forward[i].node = p->forward[i].node;
	    p->forward[i].node = q;
	    q->forward[i].distance = pos + p->forward[i].distance - k;
	    p->forward[i].distance = k + 1 - pos;
	}
    }
    l->size++;
    l->last = q;
}


void *StackRemove(SkipStack l, int k)
{
    /* Remove the k'th element.
     * Return NULL if k is out of range.
     */
    int		i, pos = 0;
    StackNode	*update[MaxLevel+1];
    StackNode	*p = l->header;
    void	*value;
    
    if (k < 1  ||  k > l->size)
	return NULL;
    
    for (i = l->level; i >= 0; i--) {
	while (pos + p->forward[i].distance < k) {
	    pos += p->forward[i].distance;
	    p = p->forward[i].node;
	}
	update[i] = p;
    }
    
    p = p->forward[0].node;
    for (i = 0; i <= l->level; i++)
	if (update[i]->forward[i].node == p) {
	    update[i]->forward[i].node = p->forward[i].node;
	    update[i]->forward[i].distance += p->forward[i].distance - 1;
	} else
	    update[i]->forward[i].distance--;
    l->size--;
    l->last = update[0];

    while(!l->header->forward[l->level].node  &&  l->level > 0)
	l->level--;
    
    value = p->value;
    free(p);
    
    return value;
}


void *StackHead(SkipStack l)
{
    l->last = l->header->forward[0].node;
    return l->last ? l->last->value : NULL;
}


void *StackNext(SkipStack l)
{
    if (l->last)
	l->last = l->last->forward[0].node;
    return l->last ? l->last->value : NULL;
}


int StackSize(SkipStack l)
{
    return l->size;
}
