/*
 * The Spread Toolkit.
 *     
 * The contents of this file are subject to the Spread Open-Source
 * License, Version 1.0 (the ``License''); you may not use
 * this file except in compliance with the License.  You may obtain a
 * copy of the License at:
 *
 * http://www.spread.org/license/
 *
 * or in the file ``license.txt'' found in this distribution.
 *
 * Software distributed under the License is distributed on an AS IS basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the 
 * License.
 *
 * The Creators of Spread are:
 *  Yair Amir, Michal Miskin-Amir, Jonathan Stanton.
 *
 *  Copyright (C) 1993-2004 Spread Concepts LLC <spread@spreadconcepts.com>
 *
 *  All Rights Reserved.
 *
 * Major Contributor(s):
 * ---------------
 *    Cristina Nita-Rotaru crisn@cs.purdue.edu - group communication security.
 *    Theo Schlossnagle    jesus@omniti.com - Perl, skiplists, autoconf.
 *    Dan Schoenblum       dansch@cnds.jhu.edu - Java interface.
 *    John Schultz         jschultz@cnds.jhu.edu - contribution to process group membership.
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "skiplist_p.h"
#include "alarm.h"

#ifndef MIN
#define MIN(a,b) ((a<b)?(a):(b))
#endif

static int get_b_rand() {
  static int ph=32; /* More bits than we will ever use */
  static unsigned long randseq;
  if(ph > 31) { /* Num bits in return of lrand48() */
    ph=0;
    randseq = get_rand();
  }
  ph++;
  return ((randseq & (1 << (ph-1))) >> (ph-1));
}

void sli_init(Skiplist *sl) {
  sl->compare = (SkiplistComparator)NULL;
  sl->comparek = (SkiplistComparator)NULL;
  sl->height=0;
  sl->preheight=0;
  sl->size=0;
  sl->top = NULL;
   sl->bottom = NULL;
  sl->index = NULL;
}

static int indexing_comp(const void *a, const void *b)
{
  void *ak = (void *) (((Skiplist *) a)->compare);
  void *bk = (void *) (((Skiplist *) b)->compare);
  assert(a);
  assert(b);
  if(ak<bk)
    return -1;
  if(ak>bk)
    return 1;
  return 0;
}
static int indexing_compk(const void *ak, const void *b)
{
  void *bk = (void *) (((Skiplist *) b)->compare);
  assert(b);
  if(ak<bk)
    return -1;
  if(ak>bk)
    return 1;
  return 0;
}

void sl_init(Skiplist *sl) {
  sli_init(sl);
  sl->index = (Skiplist *)malloc(sizeof(Skiplist));
  sli_init(sl->index);
  sl_set_compare(sl->index, indexing_comp, indexing_compk);
}

void sl_set_compare(Skiplist *sl,
		    SkiplistComparator comp,
		    SkiplistComparator compk) {
  if(sl->compare && sl->comparek) {
    sl_add_index(sl, comp, compk);
  } else {
    sl->compare = comp;
    sl->comparek = compk;
  }
}

void sl_add_index(Skiplist *sl,
		  SkiplistComparator comp,
		  SkiplistComparator compk) {
  struct skiplistnode *m;
  Skiplist *ni;
  int icount=0;
  Alarm(SKIPLIST, "Adding index to %p\n", sl);
  sl_find(sl->index, (void *)comp, &m);
  if(m) return; /* Index already there! */
  ni = (Skiplist *)malloc(sizeof(Skiplist));
  sli_init(ni);
  sl_set_compare(ni, comp, compk);
  /* Build the new index... This can be expensive! */
  m = sl_insert(sl->index, ni);
  while(m->prev) m=m->prev, icount++;
  for(m=sl_getlist(sl); m; sl_next(sl, &m)) {
    int j=icount-1;
    struct skiplistnode *nsln;
    nsln = sl_insert(ni, m->data);
    /* skip from main index down list */
    while(j>0) m=m->nextindex, j--;
    /* insert this node in the indexlist after m */
    nsln->nextindex = m->nextindex;
    if(m->nextindex) m->nextindex->previndex = nsln;
    nsln->previndex = m;
    m->nextindex = nsln;
  } 
}

struct skiplistnode *sl_getlist(Skiplist *sl) {
  if(!sl->bottom) return NULL;
  return sl->bottom->next;
}

void *sl_find(Skiplist *sl,
	      void *data,
	      struct skiplistnode **iter) {
  void *ret;
  if(!sl->compare) return 0;
  ret = sl_find_compare(sl, data, iter, sl->compare);
  return ret;
}  
void *sl_find_compare(Skiplist *sli,
		      void *data,
		      struct skiplistnode **iter,
		      SkiplistComparator comp) {
  struct skiplistnode *m = NULL;
  Skiplist *sl;
  if(comp==sli->compare || !sli->index) {
    sl = sli;
  } else {
    sl_find(sli->index, (void *)comp, &m);
    assert(m);
    sl=m->data;
  }
  sli_find_compare(sl, data, iter, sl->comparek);
  return (*iter)?((*iter)->data):(*iter);
}
int sli_find_compare(Skiplist *sl,
		     void *data,
		     struct skiplistnode **ret,
		     SkiplistComparator comp) {
  struct skiplistnode *m = NULL;
  int count=0;
  m = sl->top;
  while(m) {
    int compared = 1;
    if(m->next) compared=comp(data, m->next->data);
    if(compared == 0) {
#ifdef SL_DEBUG
      Alarm(SKIPLIST, "Looking -- found in %d steps\n", count);
#endif
      m=m->next;
      while(m->down) m=m->down;
      *ret = m;
      return count;
    }
    if((m->next == NULL) || (compared<0))
      m = m->down, count++;
    else
      m = m->next, count++;
  }
#ifdef SL_DEBUG
  Alarm(SKIPLIST, "Looking -- not found in %d steps\n", count);
#endif
  *ret = NULL;
  return count;
}
void *sl_next(Skiplist *sl, struct skiplistnode **iter) {
  if(!*iter) return NULL;
  *iter = (*iter)->next;
  return (*iter)?((*iter)->data):NULL;
}
void *sl_previous(Skiplist *sl, struct skiplistnode **iter) {
  if(!*iter) return NULL;
  *iter = (*iter)->prev;
  return (*iter)?((*iter)->data):NULL;
}
struct skiplistnode *sl_insert(Skiplist *sl,
			       void *data) {
  if(!sl->compare) return 0;
  return sl_insert_compare(sl, data, sl->compare);
}

struct skiplistnode *sl_insert_compare(Skiplist *sl,
				       void *data,
				       SkiplistComparator comp) {
  struct skiplistnode *m, *p, *tmp, *ret, **stack;
  int nh=1, ch, stacki;
  ret = NULL;
/*sl_print_struct(sl, "BI: ");*/
  if(!sl->top) {
    sl->height = 1;
    sl->topend = sl->bottomend = sl->top = sl->bottom = 
      (struct skiplistnode *)malloc(sizeof(struct skiplistnode));
    assert(sl->top);
    sl->top->next = sl->top->data = sl->top->prev =
	sl->top->up = sl->top->down = 
	sl->top->nextindex = sl->top->previndex = NULL;
    sl->top->sl = sl;
  }
  if(sl->preheight) {
    while(nh < sl->preheight && get_b_rand()) nh++;
  } else {
    while(nh <= sl->height && get_b_rand()) nh++;
  }
  /* Now we have the new height at which we wish to insert our new node */
  /* Let us make sure that our tree is a least that tall (grow if necessary)*/
  for(;sl->height<nh;sl->height++) {
    sl->top->up =
      (struct skiplistnode *)malloc(sizeof(struct skiplistnode));
    assert(sl->top->up);
    sl->top->up->down = sl->top;
    sl->top = sl->topend = sl->top->up;
    sl->top->prev = sl->top->next = sl->top->nextindex =
      sl->top->previndex = sl->top->up = NULL;
    sl->top->data = NULL;
    sl->top->sl = sl;
  }
  ch = sl->height;
  /* Find the node (or node after which we would insert) */
  /* Keep a stack to pop back through for insertion */
  m = sl->top;
  stack = (struct skiplistnode **)malloc(sizeof(struct skiplistnode *)*(nh));
  stacki=0;
  while(m) {
    int compared=-1;
    if(m->next) compared=comp(data, m->next->data);
    if(compared == 0) {
      free(stack);
      return 0;
    }
    if((m->next == NULL) || (compared<0)) {
            /* FIXME: This if ch<=nh test looks unnecessary. ch==nh at beginning of while(m)
             */
      if(ch<=nh) {
	/* push on stack */
	stack[stacki++] = m;
      }
      m = m->down;
      ch--;
    } else {
      m = m->next;
    }
  }
  /* Pop the stack and insert nodes */
  p = tmp = NULL;
  for(;stacki>0;stacki--) {
    m = stack[stacki-1];
    tmp = (struct skiplistnode *)malloc(sizeof(struct skiplistnode));
    tmp->next = m->next;
    if(m->next) m->next->prev=tmp;
    tmp->prev = m;
    tmp->up = NULL;
    tmp->nextindex = tmp->previndex = NULL;
    tmp->down = p;
    if(p) p->up=tmp;
    tmp->data = data;
    tmp->sl = sl;
    m->next = tmp;
    /* This sets ret to the bottom-most node we are inserting */
    if(!p) 
    {
            ret=tmp;
            sl->size++;
    }
    p = tmp;
  }
  free(stack);
  if(tmp && (tmp->prev == sl->topend)) {
    /* The last element on the top row is the new inserted one */
    sl->topend = tmp;
  }
  if(ret && (ret->prev == sl->bottomend)) {
    /* The last element on the bottom row is the new inserted one */
    sl->bottomend = ret;
  }
  if(sl->index != NULL) {
    /* this is a external insertion, we must insert into each index as well */
    struct skiplistnode *p, *ni, *li;
    li=ret;
    for(p = sl_getlist(sl->index); p; sl_next(sl->index, &p)) {
      ni = sl_insert((Skiplist *)p->data, ret->data);
      assert(ni);
      Alarm(SKIPLIST, "Adding %p to index %p\n", ret->data, p->data);
      li->nextindex = ni;
      ni->previndex = li;
      li = ni;
    }
  } 
  /* JRS: move size increment above to where node is inserted
    else {
    sl->size++;
  }
  */
/*sl_print_struct(sl, "AI: ");*/
  return ret;
}
struct skiplistnode *sl_append(Skiplist *sl, void *data) {
  return sl_insert(sl, data);
}
#if 0
struct skiplistnode *sl_append(Skiplist *sl, void *data) {
  int nh=1, ch, compared;
  struct skiplistnode *lastnode, *nodeago;
  if(sl->bottomend != sl->bottom) {
    compared=sl->compare(data, sl->bottomend->prev->data);
    /* If it doesn't belong at the end, then fail */
    if(compared<=0) return NULL;
  }
  if(sl->preheight) {
    while(nh < sl->preheight && get_b_rand()) nh++;
  } else {
    while(nh <= sl->height && get_b_rand()) nh++;
  }
  /* Now we have the new hieght at which we wish to insert our new node */
  /* Let us make sure that our tree is a least that tall (grow if necessary)*/
  lastnode = sl->bottomend;
  nodeago = NULL;

  if(!lastnode) return sl_insert(sl, data);

  for(;sl->height<nh;sl->height++) {
    assert(sl->top);
    sl->top->up =
      (struct skiplistnode *)malloc(sizeof(struct skiplistnode));
    sl->top->up->down = sl->top;
    sl->top = sl->topend = sl->top->up;
    sl->top->prev = sl->top->next = sl->top->nextindex =
      sl->top->previndex = NULL;
    sl->top->data = NULL;
    sl->top->sl = sl;
  }
  ch = sl->height;
  while(nh) {
    struct skiplistnode *anode;
    anode =
      (struct skiplistnode *)malloc(sizeof(struct skiplistnode));
    anode->next = lastnode;
    anode->prev = lastnode->prev;
    anode->up = NULL;
    anode->down = nodeago;
    /* If this the bottom, we are appending, so bottomend should change */
    if(!nodeago) sl->bottomend = anode;
    if(lastnode->prev) {
      if(lastnode == sl->bottom)
	sl->bottom = anode;
      else if (lastnode == sl->top)
	sl->top = anode;
    }
    nodeago = anode;
    lastnode = lastnode->up;
    nh--;
  }
  sl->size++;
  return(nodeago);
}
#endif
Skiplist *sl_concat(Skiplist *sl1, Skiplist *sl2) {
  /* Check integrity! */
  Skiplist temp;
  struct skiplistnode *b2;
  if(sl1->bottomend == NULL || sl1->bottomend->prev == NULL) {
    sl_remove_all(sl1, free);
    temp = *sl1;
    *sl1 = *sl2;
    *sl2 = temp;
    /* swap them so that sl2 can be freed normally upon return. */
    return sl1;
  }
  if(sl2->bottom == NULL || sl2->bottom->next == NULL) {
    sl_remove_all(sl2, free);
    return sl1;
  }
  b2 = sl_getlist(sl2);
  while(b2) {
    sl_insert(sl1, b2->data);
    sl_next(sl2, &b2);
  }
  sl_remove_all(sl2, NULL);
  return sl1;
}
#if 0
Skiplist *sl_concat(Skiplist *sl1, Skiplist *sl2) {
  /* Check integrity! */
  int compared, eheight;
  Skiplist temp;
  struct skiplistnode *lbottom, *lbottomend, *b1, *e1, *b2, *e2;
  if(sl1->bottomend == NULL || sl1->bottomend->prev == NULL) {
    sl_remove_all(sl1, free);
    temp = *sl1;
    *sl1 = *sl2;
    *sl2 = temp;
    /* swap them so that sl2 can be freed normally upon return. */
    return sl1;
  }
  if(sl2->bottom == NULL || sl2->bottom->next == NULL) {
    sl_remove_all(sl2, free);
    return sl1;
  }
  compared = sl1->compare(sl1->bottomend->prev->data, sl2->bottom->data);
  /* If it doesn't belong at the end, then fail */
  if(compared<=0) return NULL;
  
  /* OK now append sl2 onto sl1 */
  lbottom = lbottomend = NULL;
  eheight = MIN(sl1->height, sl2->height);
  b1 = sl1->bottom; e1 = sl1->bottomend;
  b2 = sl2->bottom; e2 = sl2->bottomend;
  while(eheight) {    
    e1->prev->next = b2;
    b2->prev = e1->prev->next;
    e2->prev->next = e1;
    e1->prev = e2->prev;
    e2->prev = NULL;
    b2 = e2;
    b1->down = lbottom;
    e1->down = lbottomend;
    if(lbottom) lbottom->up = b1;
    if(lbottomend) lbottomend->up = e1;
    
    lbottom = b1;
    lbottomend = e1;
  }
  /* Take the top of the longer one (if it is sl2) and make it sl1's */
  if(sl2->height > sl1->height) {
    b1->up = b2->up;
    e1->up = e2->up;
    b1->up->down = b1;
    e1->up->down = e1;
    sl1->height = sl2->height;
    sl1->top = sl2->top;
    sl1->topend = sl2->topend;
  }

  /* move the top pointer to here if it isn't there already */
  sl2->top = sl2->topend = b2;
  sl2->top->up = NULL; /* If it isn't already */
  sl1->size += sl2->size;
  sl_remove_all(sl2, free);
  return sl1;
}
#endif
int sl_remove(Skiplist *sl,
	      void *data, FreeFunc myfree) {
  if(!sl->compare) return 0;
  return sl_remove_compare(sl, data, myfree, sl->comparek);
}
void sl_print_struct(Skiplist *sl, char *prefix, char *(*printdata)(void *)) {
  struct skiplistnode *p, *q;
  Alarm(SKIPLIST, "Skiplist Structure (height: %d)\n", sl->height);
  p = sl->bottom;
  while(p) {
    q = p;
    Alarm(SKIPLIST, prefix);
    while(q) {
      Alarm(SKIPLIST, "%6s ", printdata(q->data));
      q=q->up;
    }
    Alarm(SKIPLIST, "\n");
    p=p->next;
  }
}
int sli_remove(Skiplist *sl, struct skiplistnode *m, FreeFunc myfree) {
  struct skiplistnode *p;
  if(!m) return 0;
  if(m->nextindex) sli_remove(m->nextindex->sl, m->nextindex, NULL);
  else sl->size--;
  /*sl_print_struct(sl, "BR:");*/
  while(m->up) m=m->up;
  while(m) {
    p=m;
    p->prev->next = p->next; /* take me out of the list */
    if(p->next) p->next->prev = p->prev; /* take me out of the list */
    m=m->down;
    /* This only frees the actual data in the bottom one */
    if(!m && myfree && p->data) myfree(p->data);
    free(p);
  }
  while(sl->top && sl->top->next == NULL) {
    /* While the row is empty and we are not on the bottom row */
    p = sl->top;
    sl->top = sl->top->down; /* Move top down one */
    if(sl->top) sl->top->up = NULL;      /* Make it think its the top */
    free(p);
    sl->height--;
  }
  if(!sl->top) sl->bottom = NULL;
  assert(sl->height>=0);
  /*sl_print_struct(sl, "AR: ");*/
  return sl->height;
}
int sl_remove_compare(Skiplist *sli,
		      void *data,
		      FreeFunc myfree, SkiplistComparator comp) {
  struct skiplistnode *m;
  Skiplist *sl;
  if(comp==sli->comparek || !sli->index) {
    sl = sli;
  } else {
    sl_find(sli->index, (void *)comp, &m);
    assert(m);
    sl=m->data;
  }
  sli_find_compare(sl, data, &m, comp);
  if (!m) return( 0 );
  while(m->previndex) m=m->previndex;
  return sli_remove(sl, m, myfree);
}
void sl_remove_all(Skiplist *sl, FreeFunc myfree) {
  /* This must remove even the place holder nodes (bottom though top)
     because we specify in the API that one can free the Skiplist after
     making this call without memory leaks */
  struct skiplistnode *m, *p, *u;
  m=sl->bottom;
  while(m) {
    p = m->next;
    if(myfree && p && p->data) myfree(p->data);
    while(m) {
      u = m->up;
      free(m);
      m=u;
    }
    m = p;
  }
  sl->top = sl->bottom = NULL;
  sl->height = 0;
  sl->size = 0;
}
void sli_destruct_free(Skiplist *sl, FreeFunc myfree) {
  sl_remove_all(sl, NULL);
  free(sl);
}
void sl_destruct(Skiplist *sl, FreeFunc myfree) {
  if(sl->index) {
    sl_remove_all(sl->index, (FreeFunc)sli_destruct_free);
    free(sl->index);
  }
  sl_remove_all(sl, myfree);
}



syntax highlighted by Code2HTML, v. 0.9.1