//
// Fursin Program - Class CFNet for networks
//
#include <iostream.h>
#include <stdlib.h>
#include <string.h>

#include "FNet.h"
#include "FPat.h"
#include "misc.h"

CFNet::CFNet(char *NameOfNet, char *NetPath, unsigned int StrMaxLen)
{
	unsigned int il1,il2;

	pFullNameOfNet=NULL;
	pNet=NULL;
	str1 = str2 = str3 = NULL;
	
	il1=strlen(NameOfNet);
	il2=strlen(NetPath);

	pFullNameOfNet = new char[il1+il2+3];

	str1 = new char[StrMaxLen];
	str2 = new char[StrMaxLen];
	str3 = new char[StrMaxLen];
		
	strcpy(pFullNameOfNet, NetPath);
	strcat(pFullNameOfNet, NameOfNet);
}

CFNet::~CFNet(void)
{
	if (pFullNameOfNet!=NULL) delete pFullNameOfNet;
	if (pNet!=NULL) delete pNet;
	if (str1!=NULL) delete str1;
	if (str1!=NULL) delete str2;
	if (str1!=NULL) delete str3;
}

//Learning of net
//Return:  0 - success
//        -1 - length of PatB and PatE is different
//        -2 - PatB > PatE
//        -3 - Dimensions of patterns are different
//        -4 - Can't save neural network
//       -11 - Can't open file with pattern
//       -12 - Unexpected end of file with pattern
//       -13 - Unknown character in pattern

int CFNet::Learn(char *PatB, char *PatE, char *PatExt,
		 char *PatPath, bool param)
{
	unsigned int ln1, ln2;
	
	unsigned int ix, iy, ix1, iy1;
	
	unsigned int nP, nPB, nPE, nPL;

	unsigned long jj, i, j;
		
	int s1, s2, sm;
	
	unsigned int ret;

	ofstream out;
	
	//Init
	ln1=strlen(PatB);
	if (strlen(PatE)!=ln1) return -1;

	nPB=hextoint(PatB);
	nPE=hextoint(PatE);
	
	if (nPB>nPE) return -2;
	
	//Array of pointers to objects with patterns
	nPL=nPE-nPB+1;
	CFPat **pFPat = new (CFPat (*[nPL]));
	
	//Creating objects with patterns
	for (nP=nPB; nP<=nPE; nP++)
	{
		strcpy(str1, PatPath);
		
		itoa(nP, str2, 16);
		strupr(str2);
		ln2=strlen(str2);
		if (ln2<ln1)
		{
			strnset(str3,'0',ln1-ln2);
			str3[ln1-ln2]='\0';
			strcat(str1,str3);
		}
		
		strcat(str1,str2);
		strcat(str1,PatExt);

		pFPat[nP-nPB]=new CFPat('.','O');
		ret=pFPat[nP-nPB]->Load(str1);
		if (ret<0) return ret-10;
	}
	
	//Checking dimensions of patterns
	ix=iy=0;
	for (nP=nPB; nP<=nPE; nP++)
	{
		ix1=pFPat[nP-nPB]->DimX();
		iy1=pFPat[nP-nPB]->DimY();
		
		if (nP==nPB)
		{
			ix=ix1;
			iy=iy1;
		}
		else if ((ix1!=ix)||(iy1!=iy)) return -3;
	}

	//Array with neural network
	jj=ix1*iy1;
	pNet = new long [jj*jj];
	
	//Learning 
	//Clearing
	for (i=0; i<jj; i++)
	{
		for (j=0; j<jj; j++)
		{
			pNet[i*jj+j]=0;
		}
	}
		
	//Filling
	for (nP=nPB; nP<=nPE; nP++)
	{
		for (i=0; i<jj; i++)
		{
			for (j=0; j<jj; j++)
			{
				s1=pFPat[nP-nPB]->Get(i);
				s2=pFPat[nP-nPB]->Get(j);
				sm=(2*s1-1)*(2*s2-1);
				pNet[i*jj+j]+=sm;
			}
		}
	}
	
	//Clearing diagonal elements
	for (i=0; i<jj; i++)
	{
		pNet[i*jj+i]=0;
	}
	
	//Saving
	out.open(pFullNameOfNet);
	if (!out) return -4;

	out << ix1 << '\n';
	out << iy1 << '\n';
	out << '\n';
	
	for (i=0; i<jj; i++)
	{
		for (j=0; j<jj; j++)
		{
			out << pNet[i*jj+j] << '\n';
		}
	}
	
	out.close();
	
	//Deleting objects with patterns ...
	for (nP=nPB; nP<=nPE; nP++)
	{
		delete pFPat[nP-nPB];
	}

	return 0;
}

//Load file with neural network
//Return:  0 - success
//        -1 - can't open file
//        -2 - Unexpected end of file
int CFNet::Load(void)
{
	ifstream inp;
	unsigned int ix1, iy1;
	unsigned long i, j, jj;

	if (pNet!=NULL) delete pNet;

	inp.open(pFullNameOfNet,ios::nocreate|ios::in);
	if (!inp) return -1;

	if (inp.eof()) 
	{
err_quit:
		inp.close();
		return -2;
	}

	inp >> ix1;
	
	if (inp.eof()) goto err_quit;
	inp >> iy1;

	jj=ix1*iy1;
	pNet = new long [jj*jj];

	for (i=0; i<jj; i++)
	{
		for (j=0; j<jj; j++)
		{
			if (inp.eof()) goto err_quit;
			inp >> pNet[i*jj+j];
		}
	}

	inp.close();

	return 0;
}

//Compare two patterns
//Return:  true  - equal
//         false - not equal
bool CFNet::Compare(CFPat *pPatInp, CFPat *pPatOut)
{
	unsigned int ix1, iy1, ix2, iy2;
	unsigned long i, jj;

	
	ix1=pPatInp->DimX();
	iy1=pPatInp->DimY();

	ix2=pPatInp->DimX();
	iy2=pPatInp->DimY();
	
	if (ix1!=ix2) return false;
	if (iy1!=iy2) return false;

	jj=ix1*iy1;

	for (i=0; i<jj; i++)
	{
		if ((pPatInp->Get(i)) != (pPatOut->Get(i))) return false;
	}

	return true;
}

//Recovering
//Return:  0 - success
//        -1 - Dimensions of patterns are different
int CFNet::Recover(CFPat *pPatInp, CFPat *pPatOut)
{
	unsigned int ix1, iy1, ix2, iy2;
	unsigned long i, j, jj;
	long sum;
	char c;

	ix1=pPatInp->DimX();
	iy1=pPatInp->DimY();

	ix2=pPatInp->DimX();
	iy2=pPatInp->DimY();
	
	if (ix1!=ix2) return -1;
	if (iy1!=iy2) return -1;

	jj=ix1*iy1;

	for (i=0; i<jj; i++)
	{
		sum=0;

		for (j=0; j<jj; j++)
		{
			sum+=pNet[i*jj+j]*(2*(pPatInp->Get(j))-1);
		}

		if (sum>=0) c=1; 
		else c=0;
		
		pPatOut->Put(i,c);
	}

	return 0;
}
