/*
   File:        ihash.c
   Author(s):   Andrew Moore
   Created:     Tue Dec 10 18:02:17 EST 2002
   Description: Mapping ivecs to doubles
   Copyright (c) Carnegie Mellon University
*/

#include "ihash.h"

void *double_copy_fn(void *old)
{
  double *old_ptr = (double *) old;
  double *nu_ptr = AM_MALLOC(double);
  *nu_ptr = *old_ptr;
  return (void *) nu_ptr;
}

void double_free_fn(void *x)
{
  double *d = (double *) x;
  AM_FREE(d,double);
}

ihash *mk_empty_ihash(void)
{
  ihash *x = AM_MALLOC(ihash);

  x -> h = mk_empty_hash_table(double_copy_fn,double_free_fn,double_num_bytes);
  x -> elnum_to_ivec = mk_empty_ivec_array();
  x -> elnum_to_value = mk_dyv(0);

  return x;
}

void free_ihash(ihash *x)
{
  free_hash_table(x->h);
  free_ivec_array(x->elnum_to_ivec);
  free_dyv(x->elnum_to_value);
  AM_FREE(x,ihash);
}

int ihash_num_bytes(ihash *ih)
{
  return sizeof(ihash) + 
    hash_table_num_bytes(ih->h) + 
    ivec_array_num_bytes(ih->elnum_to_ivec) + 
    dyv_num_bytes(ih->elnum_to_value);
}

int ihash_size(ihash *ih)
{
  return dyv_size(ih->elnum_to_value);
}

ivec *ihash_elnum_to_ivec(ihash *ih,int elnum)
{
  ivec *result = ivec_array_ref(ih->elnum_to_ivec,elnum);
#ifdef DOING_ASSERTS
  double v = dyv_ref(ih->elnum_to_value,elnum);
  bool exists;
  my_assert_always(v == ihash_lookup(ih,result,&exists));
  my_assert_always(exists);
#endif
  return result;
}

double ihash_elnum_to_value(ihash *ih,int elnum)
{
  double v = dyv_ref(ih->elnum_to_value,elnum);
#ifdef DOING_ASSERTS
  ivec *iv = ivec_array_ref(ih->elnum_to_ivec,elnum);
  bool exists;
  my_assert_always(v == ihash_lookup(ih,iv,&exists));
  my_assert_always(exists);
#endif
  return v;
}

void fprintf_ihash(FILE *s,char *m1,ihash *ih,char *m2)
{
  int i;

  for ( i = 0 ; i < ihash_size(ih) ; i++ )
  {
    ivec *iv = ihash_elnum_to_ivec(ih,i);
    double v = ihash_elnum_to_value(ih,i);
    char *string = mk_string_from_ivec(iv);
    fprintf(s,"%s[%d] = %s -> %g%s",m1,i,string,v,m2);
    free_string(string);
  }
}

void pihash(ihash *x)
{
  fprintf_ihash(stdout,"ihash",x,"\n");
}

ihash *mk_copy_ihash(ihash *old)
{
  ihash *nu = mk_empty_ihash();
  int i;
  for ( i = 0 ; i < ihash_size(old) ; i++ )
    add_to_ihash(nu,ihash_elnum_to_ivec(old,i),ihash_elnum_to_value(old,i));

  return nu;
}

void add_to_ihash(ihash *ih,ivec *iv,double value)
{
  char *string = mk_string_from_ivec(iv);
  void *data = (void *) (&value);
  if ( lookup_hash_entry(ih->h,string) != NULL )
    my_errorf("You may not add ivec %s to ihash...already there\n",string);
  add_hash_entry(ih->h,string,data);
  add_to_ivec_array(ih->elnum_to_ivec,iv);
  add_to_dyv(ih->elnum_to_value,value);
  free_string(string);
}

double ihash_lookup(ihash *ih,ivec *iv,bool *r_exists)
{
  char *string = mk_string_from_ivec(iv);
  void *data = lookup_hash_entry(ih->h,string);
  double result;

  if ( data == NULL )
  {
    result = -7e77;
    *r_exists = FALSE;
  }
  else
  {
    double *ptr = (double *) data;
    result = *ptr;
    *r_exists = TRUE;
  }

  free_string(string);

  return result;
}

void examine_ihash(ihash *ih,ivec *iv)
{
  bool exists;
  double value = ihash_lookup(ih,iv,&exists);
  char *string = mk_string_from_ivec(iv);

  if ( exists )
    printf("(%s) is in ihash with value %g\n",string,value);
  else
    printf("(%s) is not in ihash\n",string);

  free_string(string);
}

void examine_ihash_3(ihash *ih,ivec *i1,ivec *i2,ivec *i3)
{
  examine_ihash(ih,i1);
  examine_ihash(ih,i2);
  examine_ihash(ih,i3);
  wait_for_key();
}

void test_add_to_ihash(ihash *ih,ivec *iv)
{
  char *string = mk_string_from_ivec(iv);
  double value = (double) ivec_sum(iv);
  printf("Will add ivec (%s) to ihash with value %g\n",string,value);
  add_to_ihash(ih,iv,value);
  free_string(string);
}

void ihash_main(int argc,char *argv[])
{
  ivec *i1 = mk_ivec_1(8);
  ivec *i2 = mk_ivec_3(3,1,2);
  ivec *i3 = mk_ivec_2(3,1);
  ihash *ih = mk_empty_ihash();
  examine_ihash_3(ih,i1,i2,i3);
  test_add_to_ihash(ih,i1);
  examine_ihash_3(ih,i1,i2,i3);
  test_add_to_ihash(ih,i2);
  examine_ihash_3(ih,i1,i2,i3);
  test_add_to_ihash(ih,i3);
  examine_ihash_3(ih,i1,i2,i3);

  free_ivec(i1);
  free_ivec(i2);
  free_ivec(i3);

  pihash(ih);
  wait_for_key();

  free_ihash(ih);
}
