/*
   File:        occr_count.c
   Authors:     Kubica
   Created:     Mon April 7, 2003
   Description: A data structure that counts occurrences

   Copyright 2003, The Auton Lab
*/

#include "occr_count.h"


/* -------- Memory (create/destroy) functions --------- */

occr_count_item* mk_occr_count_item(int item, int init_count) {
  occr_count_item* nu = AM_MALLOC(occr_count_item);

  nu -> item = item;
  nu -> count = init_count;

  return nu;
}


occr_count_item* mk_zero_count_occr_count_item(int item) {
  occr_count_item* nu = AM_MALLOC(occr_count_item);

  nu -> item = item;
  nu -> count = 0;

  return nu;
}


occr_count_item* mk_copy_occr_count_item(occr_count_item* old)  {
  occr_count_item* nu = AM_MALLOC(occr_count_item);

  nu -> item = old -> item;
  nu -> count = old -> count;

  return nu;
}


void* mk_copy_v_occr_count_item(void* v_old)  {
  occr_count_item* old = (occr_count_item*)v_old;
  occr_count_item* nu = AM_MALLOC(occr_count_item);

  nu -> item = old -> item;
  nu -> count = old -> count;

  return (void*)nu;
}


void free_occr_count_item(occr_count_item* old) {
  AM_FREE(old,occr_count_item);
}


void free_v_occr_count_item(void* old) {
  AM_FREE((occr_count_item*)old,occr_count_item);
}

int v_occr_count_item_num_bytes(void *x)
{
  return sizeof(occr_count_item);
}

occr_count* mk_empty_occr_count(void) {
  occr_count* nu = AM_MALLOC(occr_count);

  nu -> ht = mk_empty_hash_table(mk_copy_v_occr_count_item,
				 free_v_occr_count_item,
				 v_occr_count_item_num_bytes);
  set_hash_table_key_type(nu->ht,PAULINT_KEY_TYPE);
  nu -> total_occurred = 0;
  nu -> num_items_occurred = 0;

  return nu;
}


occr_count* mk_copy_occr_count(occr_count* old) {
  occr_count* nu = AM_MALLOC(occr_count);

  nu -> ht = mk_copy_hash_table(old->ht);
  nu -> total_occurred = old -> total_occurred;
  nu -> num_items_occurred = old -> num_items_occurred;

  return nu;
}


void free_occr_count(occr_count* oc) {
  free_hash_table(oc->ht);
  AM_FREE(oc,occr_count);
}


/* -------- Access Functions --------------------------- */

void occr_count_add_occurrence(occr_count* oc, int item) {
  occr_count_add_occurrences(oc,item,1);
}


void occr_count_add_occurrences(occr_count* oc, int item, int new_counts) {
  occr_count_item* oc_it;
  void* it;

  /* try looking up the item first */
  it = lookup_hash_entry(oc->ht, (char *) &item);
  if(it) {
    oc_it = (occr_count_item*)it;
    oc_it->count += new_counts;
  } else {
    if(new_counts > 0) {
      oc_it = mk_occr_count_item(item,new_counts);
      add_hash_entry(oc->ht,(char *) &item,(void*)oc_it);
      free_occr_count_item(oc_it);

      oc->num_items_occurred += 1;
    }
  }

  oc->total_occurred += new_counts;
}


/* Add 1 to the count of each item in the ivec */
void occr_count_add_counts_for_ivec_items(occr_count* oc, ivec* items) {
  int i;

  for(i=0;i<ivec_size(items);i++) {
    occr_count_add_occurrences(oc, ivec_ref(items,i), 1);    
  }
}


void occr_count_remove_occurrence(occr_count* oc, int item) {
  occr_count_remove_occurrences(oc,item,1);
}


void occr_count_remove_occurrences(occr_count* oc, int item, int old_counts) {
  occr_count_item* oc_it;
  void* it;

  /* try looking up the item first */
  it = lookup_hash_entry(oc->ht, (char *) &item);
  if(it) {
    oc_it = (occr_count_item*)it;
    oc_it->count -= old_counts;
    oc->total_occurred -= old_counts;

    /* If this brings the count to zero, remove the item from the hash table */
    if(oc_it->count <= 0) {
      delete_hash_entry(oc->ht,(char *) &item);
      oc->num_items_occurred -= 1;
    }
  }
}


/* Remove 1 from the count of each item in the ivec */
void occr_count_remove_counts_for_ivec_items(occr_count* oc, ivec* items) {
  int i;

  for(i=0;i<ivec_size(items);i++) {
    occr_count_remove_occurrences(oc, ivec_ref(items,i), 1);    
  }
}


/* -------- Getter Functions --------------------------- */

/* The total number of occurrences */
int occr_count_total_occurred(occr_count* oc) {
  return oc->total_occurred;
}


/* The number of unique items that have occurred */
int occr_count_total_items_occurred(occr_count* oc) {
  return oc->num_items_occurred;
}


bool occr_count_occurred(occr_count* oc, int item) {
  void* it = lookup_hash_entry(oc->ht, (char *) &item);
  return (it != NULL);
}


int occr_count_times_occurred(occr_count* oc, int item) {
  occr_count_item* oc_it;
  void* it = lookup_hash_entry(oc->ht, (char *) &item);

  if(it) {
    oc_it = (occr_count_item*)it;
    return oc_it->count;
  }

  return 0;
}



occr_count_callback* mk_initial_occr_count_callback(occr_count* oc) {
  occr_count_callback* occb = AM_MALLOC(occr_count_callback);
  
  occb -> items  = mk_zero_ivec(oc->num_items_occurred);
  occb -> counts = mk_zero_ivec(oc->num_items_occurred);
  occb -> curr   = 0;

  return occb;
}


void free_occr_count_callback(occr_count_callback* occb) {
  free_ivec(occb -> items);
  free_ivec(occb -> counts);
  AM_FREE(occb,occr_count_callback);
}


void occr_count_callback_fun(const void *key, const void *data, void *extra) {
  occr_count_callback* occb = (occr_count_callback*)extra;
  occr_count_item* ocit = (occr_count_item*)data;

  ivec_set(occb->items,occb->curr,ocit->item);
  ivec_set(occb->counts,occb->curr,ocit->count);

  occb->curr += 1;
}


ivec* mk_occr_count_items_occurred(occr_count* oc) {
  occr_count_callback* occb = mk_initial_occr_count_callback(oc);
  ivec* res;

  hash_table_iterator(oc->ht, occr_count_callback_fun, (void*)occb);

  res = mk_copy_ivec(occb->items);
  
  free_occr_count_callback(occb);

  return res;
}


/* Returns a vector of length "number of unique items that have occurred"
   where item i is the number of times the time referenced in
   mk_occr_count_items_occurred has occurred
*/
ivec* mk_occr_count_items_num_times_occurred(occr_count* oc)  {
  occr_count_callback* occb = mk_initial_occr_count_callback(oc);
  ivec* res;

  hash_table_iterator(oc->ht, occr_count_callback_fun, (void*)occb);

  res = mk_copy_ivec(occb->counts);
  
  free_occr_count_callback(occb);

  return res;
}
