
//    ________________________________________________________________________
//   |      TRANS ENTRY: DATA BITFIELD	  |    DESCRIPTION     |  BITS | FLAG |
//   |____________________________________|____________________|_______|______|
//   |									  |					   |       |      |
//   |00000000 11111111 11111111 11111111 | move		       | 1-24  |   0  |
//   |00000011 00000000 00000000 00000000 | bound`s type       | 25-26 |  24  | 
//   |11111100 00000000 00000000 00000000 | reserved           | 27-32 |  26  |
//   |____________________________________|____________________|_______|______|

#define PV_BOUND		0
#define EXACT_BOUND		1
#define UPPER_BOUND		2
#define LOWER_BOUND		3
#define CLUSTER_SIZE	4

class TransEntry {
public:	
	uint64 key;
	uint32 data;
	sint16 value;
	sint8 depth;
	uint8 date;
							
	TransEntry()						{key = 0; data = value = depth = date = 0							;}
	void SetData(Move *m, uint8 type)	{data = (m->m) | (type<<24)											;}
	void GetData(Move *m, uint8 *type)	{m->m = (data & 0xFFFFFF);	*type = (data>>24)&3					;}	
};

struct TransCluster {
  TransEntry entry[CLUSTER_SIZE]; 
};

class Trans {
private:	
	TransCluster *entries;
	uint8 date;
	uint32 size;
	uint64 used;
	uint64 total;
	uint64 fullness;
public:	
	uint8 auto_side;	
	Trans()								{size = 0; used = total = fullness = 0; entries = 0; date = 0		;}
	~Trans()							{delete [] entries;													;}
	void Clear()						{memset(entries, 0, size * sizeof(TransCluster))					;}
	void NewGame()						{Clear()															;}
	void NewSearch()					{date++;	used = total = fullness = 0								;}
#ifndef AUTO
	TransEntry *FindEntry(uint64 key)	{return entries[key & (size - 1)].entry								;}
#else
	TransEntry *FindEntry(uint64 key)   {		
		int index = 2 * (key & ((size/2)-1)) + auto_side;		
		ASSERT(index <= size);
		ASSERT(index % 2 == auto_side);
		return entries[index].entry;
	}
#endif		
	void Alloc(int size_mb);	
	void SendInfo();		
	int ConvertDepth(float depth);
	float ConvertDepth(int depth);
	void Store(uint64 key, Move *m, int value, float depth, int type);
	bool Retrieve(uint64 key, Move *m, int *value, float *depth, uint8 *type);
};

void Trans::Alloc(int size_mb) {
	if(size_mb < TRANS_MIN_SIZE)	size_mb = TRANS_MIN_SIZE;
	if(size_mb > TRANS_MAX_SIZE)	size_mb = TRANS_MAX_SIZE;
	// set size - power of 2, convert Mb->b
	size = 1 << FindLastBit(size_mb) << 20;		
	size /= sizeof(TransCluster);
	delete [] entries;
	entries = new TransCluster[size];		
	Clear();	
}

void Trans::SendInfo() {
	fullness = MAX(fullness, 1000*used/total);
	fullness = MIN(fullness, 1000);
	printf("info hashfull %d\n", fullness);
	used = total = 0;
}

int Trans::ConvertDepth(float depth) {
	int ret = int(depth);
	if(ret < -127)	ret = -127;
	if(ret > 126)	ret = 126;
	return ret;
}

float Trans::ConvertDepth(int depth) {
	float ret = (float)depth;
	ASSERT(ret >= -127.0);
	ASSERT(ret <= 126.0);
	return ret;
}

void Trans::Store(uint64 key, Move *m, int value, float depth, int type) {
	uint8 t[1];	 Move move[1];
	int tmp, replace_score = 0;
	TransEntry *current, *target;
	int i, d = ConvertDepth(depth);
	current = target = FindEntry(key);
	
	for(i = 0; i < CLUSTER_SIZE; i++, current++) {
		if(current->key && current->key != key) {
			current->GetData(move, t);
			tmp = 4*(MAX_DEPTH*(date - current->date) + (MAX_DEPTH - current->depth)) + *t;
			if(tmp > replace_score)	{
				replace_score = tmp;
				target = current;
			}
		} else {
			if(current->key == key)  i++;
			target = current;
			break;
		}		
	}
	// store
	target->key = key;
	target->depth = d;
	target->date = date;
	target->value = value;
	target->SetData(m, type);
	// statistics
	used += i;
	total += CLUSTER_SIZE;
}

bool Trans::Retrieve(uint64 key, Move *m, int *value, float *depth, uint8 *type) {

	TransEntry *trans_entry;	
	trans_entry = FindEntry(key);
	
	for(int i = 0; i < CLUSTER_SIZE; i++, trans_entry++) {
		if(trans_entry->key == key) {
			trans_entry->GetData(m, type);
			*value = trans_entry->value;
			*depth = ConvertDepth(trans_entry->depth);			
			return true;
		}
	}
	m->Clear();
	return false;
}