nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
//*****************************************************************************
//
// set.h
//
// a non-ordered container that has constant lookup time.
//
//*****************************************************************************

#include <stdlib.h>
#include "list.h"
#include "set.h"

// how big of a size do our set start out at?
#define DEFAULT_SET_SIZE        5

struct set_data {
  int    num_buckets;
  int           size;
  LIST     **buckets;
  int  (* cmp)(const void *, const void *);
  int (* hash)(const void *);
};

struct set_iterator {
  int         curr_bucket; // the bucket number we're currently on
  struct set_data    *set; // the set we're iterating over
  LIST_ITERATOR *bucket_i; // the iterator for our current bucket
};



//*****************************************************************************
// local functions
//*****************************************************************************

//
// compre two elements for equality
int gen_set_cmp(const void *key1, const void *key2) {
  int val = (key2 - key1);
  if(val < 0)      return -1;
  else if(val > 0) return  1;
  else             return  0;
  return val;
}

//
// hash an item
int gen_set_hash(const void *key) {
  int val = (int)key;
  if(val < 0) return -val;
  else        return  val;
}

//
// expand a set to the new number of buckets
void setExpand(SET *set, int size) {
  // collect all of the key:value pairs
  LIST *entries = setCollect(set);
  void *entry = NULL;
  int i;

  // delete all of the current buckets
  for(i = 0; i < set->num_buckets; i++) {
    if(set->buckets[i] == NULL) continue;
    deleteList(set->buckets[i]);
  }
  free(set->buckets);

  // now, make new buckets and set them to NULL
  set->buckets = calloc(size, sizeof(LIST *));
  set->num_buckets = size;

  // now, we put all of our entries back into the new buckets
  while((entry = listPop(entries)) != NULL) {
    int bucket = set->hash(entry) % set->num_buckets;
    if(set->buckets[bucket] == NULL) set->buckets[bucket] = newList();
    listPut(set->buckets[bucket], entry);
  }
  deleteList(entries);
}



//*****************************************************************************
// implementation of set.h
//*****************************************************************************
SET *newSet(void) {
  SET *set         = calloc(1, sizeof(SET));
  set->buckets     = calloc(DEFAULT_SET_SIZE, sizeof(LIST *));
  set->num_buckets = DEFAULT_SET_SIZE;
  set->size        = 0;
  set->cmp         = gen_set_cmp;
  set->hash        = gen_set_hash;
  return set;
}

void deleteSet(SET *set) {
  int i;

  for(i = 0; i < set->num_buckets; i++)
    if(set->buckets[i] != NULL)
      deleteList(set->buckets[i]);

  free(set->buckets);
  free(set);
};

int setSize(SET *set) {
  return set->size;
}

void setPut(SET *set, void *elem) {
  // only one copy per set
  if(setIn(set, elem))
    return;

  // first, see if we'll need to expand the table
  if((set->size * 80)/100 > set->num_buckets)
    setExpand(set, (set->num_buckets * 150)/100);

  // find out what bucket we belong to
  int hash_bucket = set->hash(elem) % set->num_buckets;

  // add us to the bucket
  if(set->buckets[hash_bucket] == NULL)
    set->buckets[hash_bucket] = newList();
  // listPut ensures only one copy is in the list
  listPut(set->buckets[hash_bucket], elem);
  set->size++;
}

void *setRemove(SET *set, void *elem) {
  // find out what bucket we belong to
  int hash_bucket = set->hash(elem) % set->num_buckets;

  // see if the bucket exists
  if(set->buckets[hash_bucket] != NULL) {
    if(listRemoveWith(set->buckets[hash_bucket], elem, set->cmp)) {
      set->size--;
      return elem;
    }
  }

  return NULL;
}

int setIn(SET *set, const void *elem) {
  // find out what bucket we belong to
  int hash_bucket = set->hash(elem) % set->num_buckets;

  if(set->buckets[hash_bucket] != NULL)
    return (listGetWith(set->buckets[hash_bucket], elem, set->cmp) != NULL);
  else
    return 0;
}

LIST *setCollect(SET *set) {
  LIST *list = newList();
  int i;

  for(i = 0; i < set->num_buckets; i++) {
    if(set->buckets[i] == NULL) continue;
    LIST_ITERATOR *list_i = newListIterator(set->buckets[i]);
    void            *elem = NULL;
    ITERATE_LIST(elem, list_i) {
      listPut(list, elem);
    } deleteListIterator(list_i);
  }
  return list;
}

SET  *setCopy(SET *set) {
  SET *newset = newSet();
  setChangeHashing(newset, set->cmp, set->hash);
  setExpand(newset, set->num_buckets);
  SET_ITERATOR *set_i = newSetIterator(set);
  void          *elem = NULL;
  ITERATE_SET(elem, set_i) {
    setPut(newset, elem);
  } deleteSetIterator(set_i);
  return newset;
}

SET  *setUnion(SET *set1, SET *set2) {
  SET *newset   = NULL;
  SET *copyfrom = NULL; 
  if(set1->size > set2->size) {
    copyfrom = set2;
    newset   = setCopy(set1);
  }
  else {
    copyfrom = set1;
    newset   = setCopy(set2);
  }

  // copy all of the remaining elements
  SET_ITERATOR *set_i = newSetIterator(copyfrom);
  void          *elem = NULL;
  ITERATE_SET(elem, set_i) {
    setPut(newset, elem);
  } deleteSetIterator(set_i);

  return newset;
}

SET  *setIntersection(SET *set1, SET *set2) {
  SET *intersection = NULL;
  SET *compagainst  = NULL;
  if(set1->size > set2->size) {
    intersection = setCopy(set2);
    compagainst  = set1;
  }
  else {
    intersection = setCopy(set1);
    compagainst  = set2;
  }

  // remove everything not in the intersection
  SET_ITERATOR *set_i = newSetIterator(intersection);
  void          *elem = NULL;
  ITERATE_SET(elem, set_i) {
    if(!setIn(compagainst, elem))
      setRemove(intersection, elem);
  } deleteSetIterator(set_i);
  return intersection;
}

void setChangeHashing(SET *set, void *cmp_func, void *hash_func) {
  set->cmp  = cmp_func;
  set->hash = hash_func;
}



//*****************************************************************************
// set iterator
//
// we may sometimes want to iterate across all of the elements in a set.
// this lets us do so.
//*****************************************************************************
SET_ITERATOR *newSetIterator(SET *S) {
  SET_ITERATOR *I = malloc(sizeof(SET_ITERATOR));

  I->set = S;
  I->curr_bucket = 0;
  I->bucket_i = NULL;
  setIteratorReset(I);

  return I;
}


void deleteSetIterator(SET_ITERATOR *I) {
  if(I->bucket_i) deleteListIterator(I->bucket_i);
  free(I);
}


void setIteratorReset(SET_ITERATOR *I) {
  int i;

  if(I->bucket_i) 
    deleteListIterator(I->bucket_i);
  I->bucket_i = NULL;
  I->curr_bucket = 0;

  for(i = 0; i < I->set->num_buckets; i++) {
    if(I->set->buckets[i] == NULL)
      continue;
    if(isListEmpty(I->set->buckets[i]))
      continue;
    else {
      I->curr_bucket = i;
      I->bucket_i = newListIterator(I->set->buckets[i]);
      break;
    }
  }
}


void *setIteratorNext(SET_ITERATOR *I) {
  // we have no iterator ... we have no elements left to iterate over!
  if(I->bucket_i == NULL)
    return NULL;
  else {
    void *elem = listIteratorNext(I->bucket_i);
    // we're okay ... we have an element
    if(elem != NULL)
      return elem;
    // otherwise, we need to move onto a new bucket
    else {
      deleteListIterator(I->bucket_i);
      I->bucket_i = NULL;

      I->curr_bucket++;
      // find the next non-empty list
      for(; I->curr_bucket < I->set->num_buckets; I->curr_bucket++) {
	if(I->set->buckets[I->curr_bucket] == NULL)
	  continue;
	if(isListEmpty(I->set->buckets[I->curr_bucket]))
	  continue;
	I->bucket_i = newListIterator(I->set->buckets[I->curr_bucket]);
	break;
      }

      // we've ran out of buckets!
      if(I->curr_bucket == I->set->num_buckets)
	return NULL;
      else
	return listIteratorCurrent(I->bucket_i);
    }
  }
}


void *setIteratorCurrent(SET_ITERATOR *I) {
  // we have no elements!
  if(I->bucket_i == NULL)
    return NULL;
  else
    return listIteratorCurrent(I->bucket_i);
}