
// MeasureOfSimilarity.cpp

#include"MeasureOfSimilarity.h"
#include "IO.h"
#include "GrayCode.h"


MeasureOfSimilarity::MeasureOfSimilarity(){};
MeasureOfSimilarity::~MeasureOfSimilarity(){}

uint MeasureOfSimilarity::PartialEuclidean(vector<uint> v1, vector<uint> v2)
{
	GrayCode cGC = GrayCode();

	uint nSum = 0;
	if (v1.size() == v2.size())
	{
		for (uint n = 0; n < v1.size(); n++)
		{
			uint c = cGC.AbsoluteValue(v1[n], v2[n]);
			nSum += c * c;
		}
	}
	return nSum;
}

double MeasureOfSimilarity::EuclideanDistance(vector<uint> v1, vector<uint> v2)
{
	GrayCode cGC = GrayCode();

	uint nSum = 0;
	if (v1.size() == v2.size())
	{
		for (uint n = 0; n < v1.size(); n++)
		{
			uint c = cGC.AbsoluteValue(v1[n], v2[n]);
			nSum += c * c;
		}
	}
	return sqrt(nSum);
}

bool MeasureOfSimilarity::XORGray(bool a, bool b)
{	//	a ^= b; return a;
	if (a == b) return false;
	return true;
}

vector<bool> MeasureOfSimilarity::XORGray(vector<bool> v1, vector<bool> v2)
{	// v1 and v2 have to be gray
	vector<bool> vXOR(v1.size(), false);
	if (v1.size() == v2.size())
	{
		for (uint n = 0; n < v1.size(); n++)
		{
			vXOR[n] = XORGray(v1[n], v2[n]);
		}
	}
	return vXOR;
}

vector<bool> MeasureOfSimilarity::XORBase2(vector<bool> v1, vector<bool> v2)
{
	GrayCode cGC = GrayCode();
	vector<bool> vGray1   = cGC.Base2ToGray(v1);
	vector<bool> vGray2   = cGC.Base2ToGray(v2);
	vector<bool> vGrayXOR = XORGray(vGray1, vGray2);
	return cGC.GrayToBase2(vGrayXOR);
}

map<vector<bool>, vector<uint>> MeasureOfSimilarity::XORGray(vector<bool> vGray, map<vector<bool>, vector<uint>> mGray)
{
	GrayCode cGC = GrayCode();
	map<vector<bool>, vector<uint> > mXOR;

	map<vector<bool>, vector<uint>>::iterator i;
	for (i = mGray.begin(); i != mGray.end(); i++)
	{
		vector<bool> vNextGray = i->first;

		vector<bool> vXORGray = XORGray(vGray, vNextGray);

		mXOR.insert(make_pair(vXORGray, i->second));
	}
	return mXOR;
}

map<vector<bool>, vector<uint>> MeasureOfSimilarity::XORBase2(vector<bool> vBase2, map<vector<bool>, vector<uint>> mBase2)
{
	GrayCode cGC = GrayCode();

	vector<bool> vGray = cGC.Base2ToGray(vBase2);
	map<vector<bool>, vector<uint>> mGray = cGC.Base2ToGray(mBase2);
	map<vector<bool>, vector<uint> > mXORGray = XORGray(vGray, mGray);
	map<vector<bool>, vector<uint> > mXORBase2 = cGC.GrayToBase2(mXORGray);

	return mXORBase2;
}





uint MeasureOfSimilarity::FlipCount(map<vector<bool>, vector<uint>> m)
{
	uint nCount = 0;
	vector<uint> vPrevClass;
	map<vector<bool>, vector<uint> >::iterator i;
	for (i = m.begin(); i != m.end(); i++)
	{
		vector<uint> vIndexClass = i->second;

		if (i == m.begin())
			nCount++;
		else if (vPrevClass != vIndexClass)
		{
			nCount++;
		}
		vPrevClass = vIndexClass;
	}
	return nCount;
}

uint MeasureOfSimilarity::FlipCount(uint nClassPosition, map<vector<bool>, vector<uint>> m)
{
	uint nCount = 0;
	vector<uint> vPrevClass;
	map<vector<bool>, vector<uint> >::iterator i;
	for (i = m.begin(); i != m.end(); i++)
	{
		vector<uint> vIndexClass = i->second;

		if (vIndexClass.size() <= nClassPosition)
			return 0;

		if (i == m.begin())
			nCount++;
		else if (vPrevClass[nClassPosition] != vIndexClass[nClassPosition])
		{
			nCount++;
		}
		vPrevClass = vIndexClass;
	}
	return nCount;
}

vector<uint>  MeasureOfSimilarity::EuclideanNearestNeighbor(uint& nMinSim, vector<uint>& vIndexes, vector<uint>& vClasses, uint nIndexPosition, uint nClassPosition, vector<uint> vBase10, map<vector<uint>, vector<uint>> mTrainBase10)
{
	IO cIO = IO();


	nMinSim = 0;
	vIndexes.clear();
	vClasses.clear();
	vector<uint> vNearestBase10;

	map<vector<uint>, vector<uint> >::iterator i;
	for (i = mTrainBase10.begin(); i != mTrainBase10.end(); i++)
	{
		vector<uint> vTrainBase10 = i->first;
		vector<uint> vIndexClass = i->second;
		if (vIndexClass.size() <= nIndexPosition || vIndexClass.size() <= nClassPosition)
		{
			cout << "\n\n***Error - NearestNeighbor***\n\n";
			nMinSim = 0;
			vIndexes.clear();
			vClasses.clear();
			vNearestBase10.assign(i->first.size(), 0);
			break;
		}

		uint nSim = PartialEuclidean(vBase10, vTrainBase10);

		if (i == mTrainBase10.begin())
		{
			nMinSim = nSim;
			vIndexes.push_back(vIndexClass[nIndexPosition]);
			vClasses.push_back(vIndexClass[nClassPosition]);
			vNearestBase10 = vTrainBase10;

		}
		else if (nSim == nMinSim)
		{
			vIndexes.push_back(vIndexClass[nIndexPosition]);

			if (!cIO.FindInVector(vIndexClass[nClassPosition], vClasses))
				vClasses.push_back(vIndexClass[nClassPosition]);
		}
		else if (nSim < nMinSim)
		{
			nMinSim = nSim;
			vIndexes.clear();
			vClasses.clear();

			vIndexes.push_back(vIndexClass[nIndexPosition]);
			vClasses.push_back(vIndexClass[nClassPosition]);

			vNearestBase10 = vTrainBase10;
		}
	}
	return vNearestBase10;
}

vector<uint> MeasureOfSimilarity::EuclideanNearestNeighbor(uint nIndexPosition, uint nClassPosition, map<vector<uint>, vector<uint>>& mTest, map<vector<uint>, vector<uint>> mTrain)
{
	IO cIO = IO();

	vector<uint> vCorrectTieErrorTotal(4, 0);

	uint nMinSim = 0;
	vector<uint> vIndexes;
	vector<uint> vClasses;
	map<vector<uint>, vector<uint> >::iterator i;
	for (i = mTest.begin(); i != mTest.end(); i++)
	{
		vector<uint> vBase10 = i->first;
		vector<uint> vIndexClass = i->second;

		uint nCorrectClass = vIndexClass[nClassPosition];
		vector<uint> vNearestBase10 = EuclideanNearestNeighbor(nMinSim, vIndexes, vClasses, nIndexPosition, nClassPosition, vBase10, mTrain);

		cIO.UpDateCorrectTieErrorTotal(vCorrectTieErrorTotal, nCorrectClass, vClasses);

		vIndexClass.push_back(nMinSim);
		vIndexClass.insert(vIndexClass.end(), vIndexes.begin(), vIndexes.end());
		vIndexClass.insert(vIndexClass.end(), vClasses.begin(), vClasses.end());
		i->second = vIndexClass;
	}
	return vCorrectTieErrorTotal;
}

vector<bool> MeasureOfSimilarity::XORNearestNeighbor(uint& nMinSim, vector<uint>& vIndexes, vector<uint>& vClasses, uint nIndexPosition, uint nClassPosition, vector<bool> vTestGray, map<vector<bool>, vector<uint>> mTrainGray)
{
	IO cIO = IO();
	GrayCode cGC = GrayCode();

	nMinSim = 0;
	vIndexes.clear();
	vClasses.clear();
	vector<bool> vNearestGray;

	map<vector<bool>, vector<uint> >::iterator i;
	//cout << "\n\n";
	for (i = mTrainGray.begin(); i != mTrainGray.end(); i++)
	{
		vector<bool> vTrainGray = i->first;
		vector<uint> vIndexClass = i->second;

		if (vIndexClass.size() <= nIndexPosition || vIndexClass.size() <= nClassPosition)
		{
			cout << "\n\n***Error - NearestNeighbor***\n\n";
			nMinSim = (uint)i->first.size();
			vIndexes.clear();
			vClasses.clear();
			vNearestGray.assign(i->first.size(), false);
			break;
		}

		vector<bool> vXOR = XORGray(vTestGray, vTrainGray);
		uint nSim = cGC.GraySum(vXOR);

		//cout << nSim << " ";

		if (i == mTrainGray.begin())
		{
			nMinSim = nSim;
			vIndexes.push_back(vIndexClass[nIndexPosition]);
			vClasses.push_back(vIndexClass[nClassPosition]);
			vNearestGray = vTrainGray;
		}
		else if (nSim == nMinSim)
		{
			vIndexes.push_back(vIndexClass[nIndexPosition]);

			if (!cIO.FindInVector(vIndexClass[nClassPosition], vClasses))
				vClasses.push_back(vIndexClass[nClassPosition]);
		}
		else if (nSim < nMinSim)
		{
			nMinSim = nSim;
			vIndexes.clear();
			vClasses.clear();

			vIndexes.push_back(vIndexClass[nIndexPosition]);
			vClasses.push_back(vIndexClass[nClassPosition]);

			vNearestGray = vTrainGray;
		}
	}
	return vNearestGray;
}

vector<uint>  MeasureOfSimilarity::XORNearestNeighbor(uint nIndexPosition, uint nClassPosition, map<vector<bool>, vector<uint>>& mTestGray, map<vector<bool>, vector<uint>> mTrainGray)
{
	IO cIO = IO();

	vector<uint> vCorrectTieErrorTotal(4, 0);

	uint nMinSim = 0;
	vector<uint> vIndexes;
	vector<uint> vClasses;
	map<vector<bool>, vector<uint> >::iterator i;
	for (i = mTestGray.begin(); i != mTestGray.end(); i++)
	{
		vector<bool> vGray = i->first;
		vector<uint> vIndexClass = i->second;

		uint nCorrectClass = vIndexClass[nClassPosition];
		vector<bool> vNearestGray = XORNearestNeighbor(nMinSim, vIndexes, vClasses, nIndexPosition, nClassPosition, vGray, mTrainGray);

		cIO.UpDateCorrectTieErrorTotal(vCorrectTieErrorTotal, nCorrectClass, vClasses);

		vIndexClass.push_back(nMinSim);
		vIndexClass.insert(vIndexClass.end(), vIndexes.begin(), vIndexes.end());
		vIndexClass.insert(vIndexClass.end(), vClasses.begin(), vClasses.end());
		i->second = vIndexClass;
	}
	return vCorrectTieErrorTotal;
}

uint MeasureOfSimilarity::XORSumGray(vector<bool> v1, vector<bool> v2)
{
	GrayCode cGC = GrayCode();

	vector<bool> vXOR = XORGray(v1, v2);

	return cGC.GraySum(vXOR);
}

uint MeasureOfSimilarity::XORSumBase2(vector<bool> v1, vector<bool> v2)
{
	GrayCode cGC = GrayCode();

	v1 = cGC.Base2ToGray(v1);
	v2 = cGC.Base2ToGray(v2);
	return XORSumGray(v1, v2);
}

bool MeasureOfSimilarity::UpDateVectors(uint nTotalClasses, uint nMinSum, vector<uint> vTempIndexes, vector<uint> vTempClasses, vector<uint>& vMinSums, vector<uint>& vIndexes, vector<uint>& vClasses)
{
	if (vTempIndexes.size() == vTempClasses.size() && vMinSums.size() == nTotalClasses && vIndexes.size() == nTotalClasses && vClasses.size() == nTotalClasses)
	{
		for (uint n = 0; n < vTempClasses.size(); n++)
		{
			uint nClass = vTempClasses[n];
			if (nClass < nTotalClasses && nMinSum < vMinSums[nClass])
			{
				vMinSums[nClass] = nMinSum;
				vIndexes[nClass] = vTempIndexes[n];
				vClasses[nClass] = vTempClasses[n];
				return true;
			}
		}
	}
	return false;
}

uint MeasureOfSimilarity::XORPointClassification(uint nIndexPosition, uint nClassPosition, vector<uint>& vIndexes, vector<uint>& vClasses, vector<bool> vGray, map<vector<bool>, vector<uint>> mGray)
{	// the sum has to be done in gray 
	GrayCode cGC = GrayCode();

	uint nMinSum = (uint)vGray.size();

	map<vector<bool>, vector<uint> >::iterator i;
	for (i = mGray.begin(); i != mGray.end(); i++)
	{
		vector<bool> vTrainGray = i->first;

		uint nSum = XORSumGray(vGray, vTrainGray);

		if (i->second.size() <= nIndexPosition || i->second.size() <= nClassPosition)
		{
			cout << "\n\n***Error - MeasureOfSimilarity::XORPointClassification***\n\n ";
			vIndexes.clear();
			vClasses.clear();
			return (uint)vGray.size();;
		}

		//if (i == mGray.begin() || nSum < nMinSum)
		if (nSum < nMinSum)
		{
			nMinSum = nSum;
			vIndexes.clear();
			vClasses.clear();
			vIndexes.push_back(i->second[nIndexPosition]);
			vClasses.push_back(i->second[nClassPosition]);
		}
		else if (nSum == nMinSum)
		{
			vIndexes.push_back(i->second[nIndexPosition]);
			vClasses.push_back(i->second[nClassPosition]);
		}
	}
	return nMinSum;
}

bool MeasureOfSimilarity::XORPointClassificationAndUpDateVectors(uint nTotalClasses, uint nIndexPosition, uint nClassPosition, vector<uint>& vMinSums, vector<uint>& vIndexes, vector<uint>& vClasses, vector<bool> vGray, map<vector<bool>, vector<uint>> mGray)
{
	vector<uint> vTempIndexes;
	vector<uint> vTempClasses;

	uint nMinSum = XORPointClassification(nIndexPosition, nClassPosition, vTempIndexes, vTempClasses, vGray, mGray);
	return UpDateVectors(nTotalClasses, nMinSum, vTempIndexes, vTempClasses, vMinSums, vIndexes, vClasses);
}

vector<uint>  MeasureOfSimilarity::XORBase2PointClassification(bool& bOK, bool bPrintSumsAndIndexes, bool bMinClassification, uint nMinSumDefault, uint nIndexDefault, uint nClassDefault, uint nTotalClasses, uint nIndexPosition, uint nClassPosition, vector<uint>& vMinSums, vector<uint>& vIndexes, vector<uint>& vClasses, vector<bool> vBase2, map<vector<bool>, vector<uint>>& mGray)
{
	GrayCode cGC = GrayCode();
	vector<bool> vGray = cGC.Base2ToGray(vBase2);
	return XORGrayPointClassification(bOK, bPrintSumsAndIndexes, bMinClassification, nMinSumDefault, nIndexDefault, nClassDefault, nTotalClasses, nIndexPosition, nClassPosition, vMinSums, vIndexes, vClasses, vGray, mGray);
}

vector<uint> MeasureOfSimilarity::XORGrayPointClassification(bool& bOK, bool bPrintSumsAndIndexes, bool bMinClassification, uint nMinSumDefault, uint nIndexDefault, uint nClassDefault, uint nTotalClasses, uint nIndexPosition, uint nClassPosition, vector<uint>& vMinSums, vector<uint>& vIndexes, vector<uint>& vClasses, vector<bool> vGray, map<vector<bool>, vector<uint>>& mGray)
{
	GrayCode cGC = GrayCode();

	vector<uint> vTempIndexes;
	vector<uint> vTempClasses;

	uint nMinSum = XORPointClassification(nIndexPosition, nClassPosition, vTempIndexes, vTempClasses, vGray, mGray);
	bOK = UpDateVectors(nTotalClasses, nMinSum, vTempIndexes, vTempClasses, vMinSums, vIndexes, vClasses);

	if (bPrintSumsAndIndexes)
	{
		vMinSums.push_back(999999);
		vMinSums.insert(vMinSums.end(), vIndexes.begin(), vIndexes.end());
		vMinSums.push_back(999999);
		vMinSums.insert(vMinSums.end(), vClasses.begin(), vClasses.end());
		return vMinSums;
	}
	return vClasses;
}

vector<uint> MeasureOfSimilarity::XORPointClassification(uint& nMinSum, uint nIndexPosition, uint nClassPosition, vector<bool> vBase2, map<vector<bool>, vector<uint>> mBase2)
{// the points are bool but the sum has to be done in gray 

	nMinSum = (uint)vBase2.size();
	
	GrayCode cGC = GrayCode();
	vector<bool> vGray = cGC.Base2ToGray(vBase2);

	vector<uint> vIndexClass;
	map<vector<bool>, vector<uint> >::iterator i;
	for (i = mBase2.begin(); i != mBase2.end(); i++)
	{
		vector<bool> vNextBool = i->first;
		vector<bool> vNextGray = cGC.Base2ToGray(vNextBool);

		uint nSum = XORSumGray(vGray, vNextGray);

		if (i->second.size() <= nIndexPosition || i->second.size() <= nClassPosition)
		{
			nMinSum = (uint)vBase2.size();
			vIndexClass.clear();
			cout << "\n\n***Error - MeasureOfSimilarity::XORPointClassification***\n\n ";
			break;
		}

		if (i == mBase2.begin())
		{
			nMinSum = nSum;
			vIndexClass.clear();
			vIndexClass.push_back(i->second[nIndexPosition]);
			vIndexClass.push_back(i->second[nClassPosition]);
		}
		else if (nSum < nMinSum)
		{
			nMinSum = nSum;
			vIndexClass.clear();
			vIndexClass.push_back(i->second[nIndexPosition]);
			vIndexClass.push_back(i->second[nClassPosition]);
		}
		else if (nSum == nMinSum)
		{
			vIndexClass.push_back(i->second[nIndexPosition]);
			vIndexClass.push_back(i->second[nClassPosition]);
		}
	}
	return vIndexClass;
}

vector<uint> MeasureOfSimilarity::ExtractClassesFromIndexClasses(vector<uint> vIndexClasses)
{
	IO cIO = IO();

	vector<uint> vClasses;
	if (vIndexClasses.size() % 2 == 0)
	{
		for (uint n = 0; n < vIndexClasses.size(); n += 2)
		{
			uint nClass = vIndexClasses[n + 1];
			if (!cIO.FindInVector(nClass, vClasses))
			{
				vClasses.push_back(nClass);
			}
		}
	}
	return vClasses;
}

vector<uint> MeasureOfSimilarity::ExtractIndexesAndClassesFromIndexClasses(uint nSize, uint nClassDefault, uint nMinSum, vector<uint>& vMinSums, vector<uint>& vIndexes, vector<uint> vClasses, vector<uint> vIndexClasses)
{
	if (vClasses.size() == nSize && vIndexes.size() == nSize && vIndexClasses.size() % 2 == 0)
	{
		for (uint n = 0; n < vIndexClasses.size(); n += 2)
		{
			uint nIndex = vIndexClasses[n + 0];
			uint nClass = vIndexClasses[n + 1];

			if (vClasses[nClass] == nClassDefault)	// put in only the first occurrence
			{
				vMinSums[nClass] = nMinSum;
				vIndexes[nClass] = nIndex;
				vClasses[nClass] = nClass;
			}
		}
	}
	return vClasses;
}

vector<uint> MeasureOfSimilarity::InsertClasses(vector<uint> vNext, vector<uint> vClasses)
{
	IO cIO = IO();

	for (uint n = 0; n < vNext.size(); n++)
	{
		if (!cIO.FindInVector(vNext[n], vClasses))
		{
			vClasses.push_back(vNext[n]);
		}
	}
	return vClasses;
}

vector<uint> MeasureOfSimilarity::InsertClasses(uint nDefault, vector<uint> vNext, vector<uint> vClasses)
{
	for (uint n = 0; n < vNext.size(); n++)
	{
		uint nClass = vNext[n];
		if (nClass < vClasses.size() && nClass != nDefault)
		{
			vClasses[nClass] = nClass;
		}
	}
	return vClasses;
}

void MeasureOfSimilarity::UpDateLastClass(vector<uint>& vMinSums, vector<uint>& vIndexes, vector<uint>& vClasses, uint nTotalClasses, uint nIndexPosition, uint nClassPosition, map<vector<bool>, vector<uint>>& m, map<vector<bool>, vector<uint>> mTrainGray)
{
	GrayCode cGC = GrayCode();

	uint nMinSum;
	vector<uint> vTempIndexes;
	vector<uint> vTempClasses;
	map<vector<bool>, vector<uint>>::iterator i;
	if (m.size() > 2)
	{
		i = m.end();
		{
			i--;
			vector<bool> vGray = cGC.Base2ToGray(i->first);

			nMinSum = XORPointClassification(nIndexPosition, nClassPosition, vTempIndexes, vTempClasses, vGray, mTrainGray);
			bool bReturn = UpDateVectors(nTotalClasses, nMinSum, vTempIndexes, vTempClasses, vMinSums, vIndexes, vClasses);

			vMinSums.push_back(999999);
			vMinSums.insert(vMinSums.end(), vIndexes.begin(), vIndexes.end());
			vMinSums.push_back(999999);
			vMinSums.insert(vMinSums.end(), vClasses.begin(), vClasses.end());

			i->second = vMinSums;
		}
	}
}

vector<bool> MeasureOfSimilarity::NearestToAllXORBase2(uint& nBestMin, vector<uint>& vBestIndexClass, map<vector<bool>, vector<uint> > m)
{
	GrayCode cGC = GrayCode();

	nBestMin = 0;	// error but redon with initial if
	vBestIndexClass.assign(2, 0);

	vector<bool> vBestMinBase2;

	map<vector<bool>, vector<uint> >::iterator i;
	for (i = m.begin(); i != m.end(); i++)
	{
		vector<bool> vBase2 = i->first;
		vector<bool> vGray = cGC.Base2ToGray(vBase2);
		vector<uint> vIndexClass = i->second;

		uint nCheckMin = 0;
		map<vector<bool>, vector<uint> >::iterator ii;
		for (ii = m.begin(); ii != m.end(); ii++)
		{
			vector<bool> vvBase2 = ii->first;
			vector<bool> vvGray = cGC.Base2ToGray(vvBase2);

			vector<bool> vXOR = XORGray(vGray, vvGray);
			nCheckMin += cGC.GraySum(vXOR);
		}

		if (i == m.begin())
		{
			vBestMinBase2 = vBase2;
			nBestMin = nCheckMin;
			vBestIndexClass = vIndexClass;
		}
		else if (nBestMin > nCheckMin)
		{
			vBestMinBase2 = vBase2;
			nBestMin = nCheckMin;
			vBestIndexClass = vIndexClass;
		}
	}
	return vBestMinBase2;
}









