#include "distcache.h"
#include "global.h"
#include <iostream>

void DistCache::Store(int x, int y, REAL dist)
{
	int pos;
	if (x==y) return;
	if (!rearrange)
	{
		if (x > y)
		{
			int swp=x;
			x=y;
			y=swp;
		}
		pos=(x % Size)*Size+(y % Size);
		cells[pos].x=x;
		cells[pos].y=y;
		cells[pos].dist=dist;
	}
	else
	{
		if (alpos[x] <0 || alpos[y] <0)
		{
			cerr << "DistCache No valid Entry" <<x << " - " <<y <<endl;
			return;
		}
		pos=alpos[x]*Size+alpos[y];
		cells[pos].x=x;
		cells[pos].y=y;
		cells[pos].dist=dist;
		pos=alpos[y]*Size+alpos[x];
		cells[pos].x=y;
		cells[pos].y=x;
		cells[pos].dist=dist;
	}
}
		
bool DistCache::Get(int x, int y,REAL &dist)
{
	int pos;

	if (x==y) 
	{
		dist=0;
		return true;
	}
	if (!rearrange)
	{
		if (x > y)
		{
			int swp=x;
			x=y;
			y=swp;
		}
		pos=(x % Size)*Size+(y % Size);
	}
	else
	{
		if (alpos[x] <0 || alpos[y] <0)
		{
			cerr << "DistCache No valid Entry" <<x << " - " <<y <<endl;
			return false;
		}
		pos=alpos[x]*Size+alpos[y];
	}
	if (cells[pos].x!=x || cells[pos].y!=y) return false;
	dist=cells[pos].dist;
	return true;
}

void DistCache::InitArrange(int Nseqs)
{
	alpos=(int *)Malloc(2*Nseqs*sizeof(int));
	for (int i=0; i<2*Nseqs;i++)
	{
		alpos[i]=-1;
	}
}

void DistCache::DoReArrange(int Ncls)
{
	int *almap;
	int i,j;
	int temp;
	int getx,gety,pos,putx,puty;
	REAL tempv,getv;
	
	almap=(int *)Malloc(Size*sizeof(int));
	
	for (i=0;i<Ncls;i++)
	{
			if (alpos[i] >=0)	almap[alpos[i]]=i;
	}
	
	for (i=0;i<Size-1;i++)
		for (j=i+1;j<Size;j++)
	{
		pos=i*Size+j;
		tempv=cells[pos].dist;
		putx=alpos[cells[pos].x];
		if (almap[putx]!=cells[pos].x) putx=-1;
		puty=alpos[cells[pos].y];
		if (almap[puty]!=cells[pos].y) puty=-1;
		if (cells[pos].x==almap[i] && cells[pos].y==almap[j])
			continue;
		
		getx=almap[i];
		gety=almap[j];
		if (getx > gety)
		{
			int swp=getx;
			getx=gety;
			gety=swp;
		}
		pos=(getx %Size)*Size+(gety %Size);
		if (cells[pos].x==getx && cells[pos].y==gety)
		{
			cells[i*Size+j].x=getx;
			cells[i*Size+j].y==gety;
			cells[i*Size+j].dist=cells[pos].dist;
		}
		while (putx >=0 && puty >=0 && putx >i || (putx ==i && puty > j))
		{
			getx=putx;gety=puty;getv=tempv;
			pos=putx*Size+puty;
			tempv=cells[pos].dist;
			putx=alpos[cells[pos].x];
			if (almap[putx]!=cells[pos].x) putx=-1;
			puty=alpos[cells[pos].y];
			if (almap[puty]!=cells[pos].y) puty=-1;
			cells[pos].x=getx;
			cells[pos].y=gety;
			cells[pos].dist=getv;	
		}
	}
	for (i=0;i<Size-1;i++)
		for (j=i+1;j<Size;j++)
	{
		pos=i*Size+j;
		if (cells[pos].x==almap[i] && cells[pos].y==almap[j])
			{
				cells[j*Size+i].x=almap[j];
				cells[j*Size+i].y=almap[i];
				cells[j*Size+i].dist=cells[pos].dist;
			}
	}
	rearrange=true;
	free(almap);
}

void DistCache::Update(int clr1,int clr2,int clrnew,int frq1,int frq2,int Dstyle)
{
	int slot1,slot2;
	int i;
	REAL dist;
	
	slot1=alpos[clr1];
	slot2=alpos[clr2];
	for (i=0;i<Size;i++)
	{
		if (i!=slot1 && i !=slot2)
		{
			int pos1=i*Size+slot1;
			int pos2=i*Size+slot2;
			if (cells[pos1].x==cells[pos2].x && cells[pos1].y==clr1 &&cells[pos2].y==clr2)
			{
				if (Dstyle==DS_Ave)
					dist=(cells[pos1].dist*frq1+cells[pos2].dist*frq2)/(frq1+frq2);
				else
					dist=max(cells[pos1].dist,cells[pos2].dist);
				cells[pos2].y=clrnew;
				cells[pos2].dist=dist;
				pos2=slot2*Size+i;
				cells[pos2].x=clrnew;
				cells[pos2].dist=dist;
			}
		}
	}
	alpos[clrnew]=slot2;
}
