/*
  This is Version 2.0 of Rich Sutton's Tile Coding Software
  available from his website at:
  http://www.richsutton.com
*/

/* 
External documentation and recommendations on the use of this code is
available at http://www.cs.umass.edu/~rich/tiles.html.

This is an implementation of grid-style tile codings, based originally on 
the UNH CMAC code (see http://www.ece.unh.edu/robots/cmac.htm). 
Here we provide a procedure, "GetTiles", that maps floating-point and integer
variables to a list of tiles. This function is memoryless and requires no
setup. We assume that hashing colisions are to be ignored. There may be
duplicates in the list of tiles, but this is unlikely if memory-size is
large. 

The floating-point input variables will be gridded at unit intervals, so generalization
will be by 1 in each direction, and any scaling will have 
to be done externally before calling tiles.  There is no generalization
across integer values.

It is recommended by the UNH folks that num-tilings be a power of 2, e.g., 16. 

We assume the existence of a function "rand()" that produces successive
random integers, of which we use only the low-order bytes.
*/

#include <iostream>
#include <cmath>
#include "tiles2.h"

void GetTiles(
	      int tiles[],               // provided array contains returned tiles (tile indices)
	      int num_tilings,           // number of tile indices to be returned in tiles       
	      int memory_size,           // total number of possible tiles
	      float floats[],            // array of floating point variables
	      int num_floats,            // number of floating point variables
	      int ints[],				   // array of integer variables
	      int num_ints)              // number of integer variables
{
  int i,j;
  int qstate[MAX_NUM_VARS];
  int base[MAX_NUM_VARS];
  int coordinates[MAX_NUM_VARS * 2 + 1];   /* one interval number per relevant dimension */
  int num_coordinates = num_floats + num_ints + 1;
  
  for (int i=0; i<num_ints; i++) coordinates[num_floats+1+i] = ints[i];
  
  /* quantize state to integers (henceforth, tile widths == num_tilings) */
  for (i = 0; i < num_floats; i++) {
    qstate[i] = (int) floor(floats[i] * num_tilings);
    base[i] = 0;
  }
  
  /*compute the tile numbers */
  for (j = 0; j < num_tilings; j++) {
    
    /* loop over each relevant dimension */
    for (i = 0; i < num_floats; i++) {
      
      /* find coordinates of activated tile in tiling space */
      if (qstate[i] >= base[i])
	coordinates[i] = qstate[i] - ((qstate[i] - base[i]) % num_tilings);
      else
	coordinates[i] = qstate[i]+1 + ((base[i] - qstate[i] - 1) % num_tilings) - num_tilings;
      
      /* compute displacement of next tiling in quantized space */
      base[i] += 1 + (2 * i);
    }
    /* add additional indices for tiling and hashing_set so they hash differently */
    coordinates[i] = j;
    
    tiles[j] = hash_UNH(coordinates, num_coordinates, memory_size, 449);
  }
  return;
}

			
void GetTiles(
	      int tiles[],               // provided array contains returned tiles (tile indices)
	      int num_tilings,           // number of tile indices to be returned in tiles       
	      collision_table *ctable,    // total number of possible tiles
	      float floats[],            // array of floating point variables
	      int num_floats,            // number of floating point variables
	      int ints[],				   // array of integer variables
	      int num_ints)              // number of integer variables
{
  int i,j;
  int qstate[MAX_NUM_VARS];
  int base[MAX_NUM_VARS];
  int coordinates[MAX_NUM_VARS * 2 + 1];   /* one interval number per relevant dimension */
  int num_coordinates = num_floats + num_ints + 1;
  
  for (int i=0; i<num_ints; i++) coordinates[num_floats+1+i] = ints[i];
  
  /* quantize state to integers (henceforth, tile widths == num_tilings) */
  for (i = 0; i < num_floats; i++) {
    qstate[i] = (int) floor(floats[i] * num_tilings);
    base[i] = 0;
  }
  
  /*compute the tile numbers */
  for (j = 0; j < num_tilings; j++) {
    
    /* loop over each relevant dimension */
    for (i = 0; i < num_floats; i++) {
      
      /* find coordinates of activated tile in tiling space */
      if (qstate[i] >= base[i])
	coordinates[i] = qstate[i] - ((qstate[i] - base[i]) % num_tilings);
      else
	coordinates[i] = qstate[i]+1 + ((base[i] - qstate[i] - 1) % num_tilings) - num_tilings;
      
      /* compute displacement of next tiling in quantized space */
      base[i] += 1 + (2 * i);
    }
    /* add additional indices for tiling and hashing_set so they hash differently */
    coordinates[i] = j;
    
    tiles[j] = hash(coordinates, num_coordinates,ctable);
  }
  return;
}


/* hash_UNH
   Takes an array of integers and returns the corresponding tile after hashing 
*/


int hash_UNH(int *ints, int num_ints, long m, int increment)
{
	static unsigned int rndseq[2048];
	static int first_call =  1;
	int i,k;
	long index;
	long sum = 0;
	
	/* if first call to hashing, initialize table of random numbers */
    if (first_call) {
		for (k = 0; k < 2048; k++) {
			rndseq[k] = 0;
			for (i=0; i < (int) sizeof(int); ++i)
	    		rndseq[k] = (rndseq[k] << 8) | (rand() & 0xff);    
		}
		first_call = 0;
    }

	for (i = 0; i < num_ints; i++) {
		/* add random table offset for this dimension and wrap around */
		index = ints[i];
		index += (increment * i);
		index %= 2048;
		while (index < 0) index += 2048;
			
		/* add selected random number to sum */
		sum += (long)rndseq[(int)index];
	}
	index = (int)(sum % m);
	while (index < 0) index += m;
		
	return(index);
}


int hash(int *ints, int num_ints, collision_table *ct);

/* hash
   Takes an array of integers and returns the corresponding tile after hashing 
*/
int hash(int *ints, int num_ints, collision_table *ct)
{
	int j;
	long ccheck;

	ct->calls++;
	j = hash_UNH(ints, num_ints, ct->m, 449);
	ccheck = hash_UNH(ints, num_ints, MaxLONGINT, 457);
	if (ccheck == ct->data[j])
	    ct->clearhits++;
	else if (ct->data[j] == -1) {
		ct->clearhits++;
	    ct->data[j] = ccheck; }
	else if (ct->safe == 0)
		ct->collisions++;
	else {
		long h2 = 1 + 2 * hash_UNH(ints,num_ints,(MaxLONGINT)/4,449);
		int i = 0;
		while (++i) {
			ct->collisions++;
			j = (j+h2) % (ct->m);
			//printf("(%d)",j);
			if (i > ct->m) {printf("\nOut of Memory"); exit(0);}
			if (ccheck == ct->data[j]) break;
			if (ct->data[j] == -1) {ct->data[j] = ccheck; break;}
		}
	}			
	return j;
}

void collision_table::reset() {
    for (int i=0; i<m; i++) data[i] = -1;
    calls = 0;
    clearhits = 0;
    collisions = 0;
}

collision_table::collision_table(int size, int safety) {
  int tmp = size;
  while (tmp > 2){
    if (tmp % 2 != 0) {
      printf("\nSize of collision table must be power of 2 %d",size);
      exit(0);
    }
    tmp /= 2;
  }
  data = new long[size];
  m = size;
  safe = safety;
  reset();
}

collision_table::~collision_table() {
	delete[] data;
}

int collision_table::usage() {
	int count = 0;
	for (int i=0; i<m; i++) if (data[i] != -1) count++;
	return count;
}

void collision_table::save(char *fileName, unsigned long pos) {
  
  std::fstream file;
  file.open(fileName, std::ios::out | std::ios::binary | std::ios::app);

  file.seekp(pos, std::ios::beg);

  file.write((char *) &m, sizeof(long));
  file.write((char *) &safe, sizeof(int));
  file.write((char *) &calls, sizeof(long));
  file.write((char *) &clearhits, sizeof(long));
  file.write((char *) &collisions, sizeof(long));
  file.write((char *) data, m*sizeof(long));
  
  file.close();
}

void collision_table::restore(char *fileName, unsigned long pos) {

  std::fstream file;
  file.open(fileName, std::ios::in | std::ios::binary);

  file.seekg(pos, std::ios::beg);

  file.read((char *) &m, sizeof(long));
  file.read((char *) &safe, sizeof(int));
  file.read((char *) &calls, sizeof(long));
  file.read((char *) &clearhits, sizeof(long));
  file.read((char *) &collisions, sizeof(long));
  file.read((char *) data, m*sizeof(long));

  file.close();
}

/*
void collision_table::save(char *filename) {
	write(open(filename, O_BINARY | O_CREAT | O_WRONLY);
};
    
void collision_table::restore(char *filename) {
	read(open(filename, O_BINARY | O_CREAT | O_WRONLY);
}
*/
   
    
int i_tmp_arr[MAX_NUM_VARS];
float f_tmp_arr[MAX_NUM_VARS];

// No ints
void GetTiles(int tiles[],int nt,int memory,float floats[],int nf) {
    GetTiles(tiles,nt,memory,floats,nf,i_tmp_arr,0);
}
void GetTiles(int tiles[],int nt,collision_table *ct,float floats[],int nf) {
    GetTiles(tiles,nt,ct,floats,nf,i_tmp_arr,0);
}

//one int
void GetTiles(int tiles[],int nt,int memory,float floats[],int nf,int h1) {
    i_tmp_arr[0]=h1;
    GetTiles(tiles,nt,memory,floats,nf,i_tmp_arr,1);
}
void GetTiles(int tiles[],int nt,collision_table *ct,float floats[],int nf,int h1) {
    i_tmp_arr[0]=h1;
    GetTiles(tiles,nt,ct,floats,nf,i_tmp_arr,1);
}

// two ints
void GetTiles(int tiles[],int nt,int memory,float floats[],int nf,int h1,int h2) {
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    GetTiles(tiles,nt,memory,floats,nf,i_tmp_arr,2);
}
void GetTiles(int tiles[],int nt,collision_table *ct,float floats[],int nf,int h1,int h2) {
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    GetTiles(tiles,nt,ct,floats,nf,i_tmp_arr,2);
}

// three ints
void GetTiles(int tiles[],int nt,int memory,float floats[],int nf,int h1,int h2,int h3) {
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    i_tmp_arr[2]=h3;
    GetTiles(tiles,nt,memory,floats,nf,i_tmp_arr,3);
}
void GetTiles(int tiles[],int nt,collision_table *ct,float floats[],int nf,int h1,int h2,int h3) {
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    i_tmp_arr[2]=h3;
    GetTiles(tiles,nt,ct,floats,nf,i_tmp_arr,3);
}

// one float, No ints
void GetTiles1(int tiles[],int nt,int memory,float f1) {
    f_tmp_arr[0]=f1;
    GetTiles(tiles,nt,memory,f_tmp_arr,1,i_tmp_arr,0);
}
void GetTiles1(int tiles[],int nt,collision_table *ct,float f1) {
    f_tmp_arr[0]=f1;
    GetTiles(tiles,nt,ct,f_tmp_arr,1,i_tmp_arr,0);
}

// one float, one int
void GetTiles1(int tiles[],int nt,int memory,float f1,int h1) {
    f_tmp_arr[0]=f1;
    i_tmp_arr[0]=h1;
    GetTiles(tiles,nt,memory,f_tmp_arr,1,i_tmp_arr,1);
}
void GetTiles1(int tiles[],int nt,collision_table *ct,float f1,int h1) {
    f_tmp_arr[0]=f1;
    i_tmp_arr[0]=h1;
    GetTiles(tiles,nt,ct,f_tmp_arr,1,i_tmp_arr,1);
}

// one float, two ints
void GetTiles1(int tiles[],int nt,int memory,float f1,int h1,int h2) {
    f_tmp_arr[0]=f1;
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    GetTiles(tiles,nt,memory,f_tmp_arr,1,i_tmp_arr,2);
}
void GetTiles1(int tiles[],int nt,collision_table *ct,float f1,int h1,int h2) {
    f_tmp_arr[0]=f1;
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    GetTiles(tiles,nt,ct,f_tmp_arr,1,i_tmp_arr,2);
}

// one float, three ints
void GetTiles1(int tiles[],int nt,int memory,float f1,int h1,int h2,int h3) {
    f_tmp_arr[0]=f1;
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    i_tmp_arr[2]=h3;
    GetTiles(tiles,nt,memory,f_tmp_arr,1,i_tmp_arr,3);
}
void GetTiles1(int tiles[],int nt,collision_table *ct,float f1,int h1,int h2,int h3) {
    f_tmp_arr[0]=f1;
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    i_tmp_arr[2]=h3;
    GetTiles(tiles,nt,ct,f_tmp_arr,1,i_tmp_arr,3);
}

// two floats, No ints
void GetTiles2(int tiles[],int nt,int memory,float f1,float f2) {
    f_tmp_arr[0]=f1;
    f_tmp_arr[1]=f2;
    GetTiles(tiles,nt,memory,f_tmp_arr,2,i_tmp_arr,0);
}
void GetTiles2(int tiles[],int nt,collision_table *ct,float f1,float f2) {
    f_tmp_arr[0]=f1;
    f_tmp_arr[1]=f2;
    GetTiles(tiles,nt,ct,f_tmp_arr,2,i_tmp_arr,0);
}

// two floats, one int
void GetTiles2(int tiles[],int nt,int memory,float f1,float f2,int h1) {
    f_tmp_arr[0]=f1;
    f_tmp_arr[1]=f2;
    i_tmp_arr[0]=h1;
    GetTiles(tiles,nt,memory,f_tmp_arr,2,i_tmp_arr,1);
}
void GetTiles2(int tiles[],int nt,collision_table *ct,float f1,float f2,int h1) {
    f_tmp_arr[0]=f1;
    f_tmp_arr[1]=f2;
    i_tmp_arr[0]=h1;
    GetTiles(tiles,nt,ct,f_tmp_arr,2,i_tmp_arr,1);
}

// two floats, two ints
void GetTiles2(int tiles[],int nt,int memory,float f1,float f2,int h1,int h2) {
    f_tmp_arr[0]=f1;
    f_tmp_arr[1]=f2;
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    GetTiles(tiles,nt,memory,f_tmp_arr,2,i_tmp_arr,2);
}
void GetTiles2(int tiles[],int nt,collision_table *ct,float f1,float f2,int h1,int h2) {
    f_tmp_arr[0]=f1;
    f_tmp_arr[1]=f2;
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    GetTiles(tiles,nt,ct,f_tmp_arr,2,i_tmp_arr,2);
}

// two floats, three ints
void GetTiles2(int tiles[],int nt,int memory,float f1,float f2,int h1,int h2,int h3) {
    f_tmp_arr[0]=f1;
    f_tmp_arr[1]=f2;
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    i_tmp_arr[2]=h3;
    GetTiles(tiles,nt,memory,f_tmp_arr,2,i_tmp_arr,3);
}
void GetTiles2(int tiles[],int nt,collision_table *ct,float f1,float f2,int h1,int h2,int h3) {
    f_tmp_arr[0]=f1;
    f_tmp_arr[1]=f2;
    i_tmp_arr[0]=h1;
    i_tmp_arr[1]=h2;
    i_tmp_arr[2]=h3;
    GetTiles(tiles,nt,ct,f_tmp_arr,2,i_tmp_arr,3);
}