/****************************************************************************

    Neural Network Library example program
    Copyright (C) 1998 Daniel Franklin
	Totally hacked (:) 2002 Vladimir Smirnov

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 ****************************************************************************/

#include <nnwork.h>
#include <iostream.h>
#include <iomanip.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#ifdef __linux
#include <unistd.h>
#endif

#include <v_lib.h>

#define NITER	1000
#define INDIM	2
#define NPAT	7
#define	SA_POW	3

vec_t patterns_original[NPAT][INDIM] = {
	{0.4,0.5},
	{0.7,0.5},
	{0.4,0.8},
	{0.6,0.5},
	{0.55,0.65},
	{0.4,0.7},
	{0.5,0.6}
};

vec_t patterns[NPAT][INDIM];

void setup_patterns(void)
{
	int i;
	vec2_t a = {0.4, 0.5};
	vec2_t new_a = {0.05, 0.05};
	
	for(i=0;i<NPAT;i++)
	{
		vec2Sub(patterns[i],patterns_original[i],a);
		vec2Scale(patterns[i],patterns[i],3);
		vec2Add(patterns[i],patterns[i],new_a);
	}
}

vec_t desired[NPAT] = {
	0.62,
	0.47,
	0.68,
	
	0.74,
	0.55,
	0.69,
	
	0.95
};

int permute_table[NPAT];

#ifdef __linux

void rnd_setup(void)
{
	unsigned int s;
	int rnddev=open("/dev/random",0);
	read(rnddev,&s,2);
	close(rnddev);
	srand(s);
}

int v_rand(int num)
{
	return (rand() >> 2) % num;
}

#else

void rnd_setup(void)
{
	srand(timeTickCount());
}

int v_rand(int num)
{
	return (rand() >> 2) % num;
}

#endif

void permute(void)
{
	int i;
/*	puts("permute(): generating...");
	fflush(stdout);
*/	for(i=0;i<NPAT;i++)
		permute_table[i]=i;
		
/*		
	for(i=0;i<NPAT;i++)
	{
		for(;;) {
			int ind = v_rand(NPAT);
			if(permute_table[ind]==-1)
			{
				permute_table[ind] = i;
				break;
			}
		}
	}*/
/*	printf("permute(): ");
	for(i=0;i<NPAT;i++)
		printf("%d\t",permute_table[i]);
	puts("");
	fflush(stdout);*/
}

vec_t anneal(int i)
{
	return exp(- SA_POW * ((vec_t)i / (vec_t)(NITER)));
}

int grid_neural(nnwork &model)
;

int main(int argc,char *argv[])
{
	rnd_setup();
	
	setup_patterns();
	
	nnwork model(INDIM,7,1);
	vec_t result [10];
	int i, j;

	if(argc==2)
		model.load(argv[1]);
	else
	{
    	for(i=0;i<NITER;i++)
    	{
    		permute();
    		vec_t msr = 0.00005 * (NITER / (i+1));
    		for (j = 0; j < NPAT; j++) {
    			model.train (patterns[permute_table[j]], desired+permute_table[j],
    				msr, 0.3 * anneal(i));
    		}
    		printf("*");
    	}
   		printf("\n");
    	model.save("weights");
	}
	
	for (j = 0; j < NPAT; j++) {
		model.run(patterns[j], result);
		printf("Test %d; should be %f, got %f\n",j,desired[j],result[0]);
	}

#if 1

	vec2_t a,b,c,dab,dac,px,py,p;
	int subdiv = 50;
	
	vec2Copy(a,patterns[0]);
	vec2Copy(b,patterns[1]);
	vec2Copy(c,patterns[2]);
	
	vec2Sub(dab,b,a);
	vec2Sub(dac,c,a);
	
	vec2Scale(dab,dab,1.0f/subdiv);
	vec2Scale(dac,dac,1.0f/subdiv);
	
	subdiv++;
	
	result[0] = 0;
	
	FILE *fout=fopen("out","wt");
	for(j=0;j<subdiv;j++) /* y * 100 */
	{
		vec2Scale(py,dac,j);
		for(i=0;i<(subdiv-j);i++) 
		{
			vec2Scale(px,dab,i);
			vec2Add(p,px,py);
			vec2Add(p,p,a);
			
			model.run(p,result);
			fprintf(fout,"%g;%g;%g\n",p[0],p[1],result[0]);
		}
	}
#endif
	grid_neural(model);
	
	return 0;
}

tripatch_p ppTriangulate2D(vec3_t center,vec_t radius,vec_t angle0,
	int split,vboolean extinfo,vec_t x,vec_t y,vec_t dx,vec_t dy,vec_t (*proc)(vec_t x,vec_t y,nnwork &model),
	nnwork &model
)
{
	int i;
	tripatch_p pg, p;
	vec3_t curvert;
	vec3_t real_abc[3];
	vec3_t bma,bmb,bmc;

	for(i=0;i<3;i++)
	{
		vec3Copy(real_abc[i],center);
		real_abc[i][0]+=radius*(vec_t)cos(angle0+M_2PI*i/3);
		real_abc[i][1]+=radius*(vec_t)sin(angle0+M_2PI*i/3);
	}
	vec3Set(bma,x,y,0);
	vec3Set(bmb,x+dx,y,0);
	vec3Set(bmc,x,y+dy,0);

	pg = patCreateTriangular(bma,bmb,bmc,split,0);
	p = patCreateTriangular(real_abc[0],real_abc[1],real_abc[2],split,true);

	for(i=0;i<p->numvert;i++)
	{
		vec3Copy(curvert,p->vlist[i]);
		p->vlist[i][2]=proc(pg->vlist[i][0],pg->vlist[i][1],model);
	}

	patFree(pg);

	if(extinfo)
		patDefaultGeometry(p);

	return p;
}

void patSaveDXF(tripatch_p p,char *name)
{
	u32_t i, nt = p->numtri;
	vec3_t *vlist = p->vlist;
	itriple_t *tlist = p->tlist;
	vhandle_t out = dxfOpenWrite(name);
	for(i=0;i<nt;i++)
		dxfTriangle(out,vlist[tlist[i][0]],vlist[tlist[i][1]],vlist[tlist[i][2]]);
	dxfClose(out);
}

vec_t nngridproc(vec_t x,vec_t y,nnwork &model)
{
	vec_t result = 0;
	vec2_t xy;
	vec2Set(xy,x,y);
	model.run(xy,&result);
	return result * 100;
}

vec_t base = 0;

vec_t procbase(vec_t x,vec_t y,nnwork &model)
{
	return base;
}

#define ON_EPSILON      ((vec_t)(1e-5))
#define DIFF_EPSILON    ((vec_t)(1e-7))

/*
	========================
	triSplitPoints
	Треугольник: vlist[indices[0]] - vlist[indices[2]]
	Результат: false если пересечения нет, иначе true. В последнем случае
	две точки пересечения с плоскостью pl (возможно, совпадающие)
	помещаются в dest[0] и dest[1]
	========================
*/

vboolean	V_LOCAL
triSplitPoints(vec3_t dest[],vec3_t vlist[],itriple_t indices,plane3_p pl)
{
	const int SIDE_ON = 0;
	const int SIDE_FRONT = 1;
	const int SIDE_BACK = 2;
	int sides[3]={0,0,0};
	int counts[3]={0,0,0};
	int ind,i,i1,i2;
	vec_t dist;
	line3_t l1,l2;
	const vec_t on_epsilon=(vec_t)ON_EPSILON;
	for(i=0;i<3;i++)
	{
		dist=vec3Dot(pl->normal,vlist[indices[i]])+pl->dist;
		if(dist>on_epsilon)
		{
			sides[i]=SIDE_FRONT;
			counts[SIDE_FRONT]++;
		}
		else if(dist<(-on_epsilon))
		{
			sides[i]=SIDE_BACK;
			counts[SIDE_BACK]++;
		}
		else
		{
			sides[i]=SIDE_ON;
			counts[SIDE_ON]++;
		}
	}
	if(!counts[SIDE_ON])
	{
		/* Плоскости не касается ни одна из вершин */
		if(counts[SIDE_FRONT]<counts[SIDE_BACK]) 
			ind=SIDE_FRONT;
		else
			ind=SIDE_BACK;
		for(i=0;i<3;i++)
			if(sides[i]==ind) break;
		switch(i)
		{
			case 0: i1=1; i2=2; break;
			case 1: i1=0; i2=2; break;
			case 2: i1=0; i2=1; break;
			/* all vertices either in front, or beyond the plane */
			default: return false; 
		}
		/* compute two lines, than cross them with plane */
		line3FromPoints(&l1,vlist[indices[i]],vlist[indices[i1]]);
		line3FromPoints(&l2,vlist[indices[i]],vlist[indices[i2]]);
		vec3FromLinePlane(dest[0],&l1,pl,NULL);
		vec3FromLinePlane(dest[1],&l2,pl,NULL);
		return true;
	}
	if(counts[SIDE_ON]>1)
	{
		/* take first 2 vertices which are laying on plane */
		for(ind=i=0;i<3;i++)
		{
			if(sides[i]==SIDE_ON)
			{
				vec3Copy(dest[ind],vlist[indices[i]]);
				if(ind++==3) break;
			}
		}
		return true;
	}
	/* counts[SIDE_ON]==1
	 * take one vertex which is laying on plane
	 */
	for(i=0;i<3;i++)
		if(sides[i]==SIDE_ON)
		{
			vec3Copy(dest[0],vlist[indices[i]]);
			break;
		}
	/* find cross between plane and line which is based on other 
	 * vertices
	 */
	switch(i)
	{
		case 0: i1=1; i2=2; break;
		case 1: i1=0; i2=2; break;
		default: i1=0; i2=1; break;
	}
	if(sides[i1]==sides[i2]) /* triangle only touches the plane */
	{
		vec3Copy(dest[1],dest[0]);
		return true;
	}
	line3FromPoints(&l1,vlist[indices[i1]],vlist[indices[i2]]);
	vec3FromLinePlane(dest[1],&l1,pl,NULL);
	return true;
}

#ifdef  OLD_SIDE_ON
#define SIDE_ON       OLD_SIDE_ON
#define SIDE_FRONT    OLD_SIDE_FRONT
#define SIDE_BACK     OLD_SIDE_BACK
#undef  OLD_SIDE_ON
#undef  OLD_SIDE_FRONT
#undef  OLD_SIDE_BACK
#else
#undef  SIDE_ON
#undef  SIDE_FRONT
#undef  SIDE_BACK
#endif

/*
	==========================
	patIntersectWithPlane
	Пересечение триангулированной сетки с плоскостью.
	В Dest помещается указатель на распределенный блок, содержащий пары 
	точек (возможно, совпадающих) - результатов пересечений для каждого
	треугольника сетки.
	Используйте в комплексе с uuRemoveDegenerateLines.
	==========================
*/

V_LOCAL void
patIntersectWithPlane(vec3_t **Dest,int *numpairs,tripatch_p pat,
	plane3_p pl)
{
	fifo_t pf;
	vec3_t pair[2];
	int i;
	fifoInit(&pf,2*sizeof(vec3_t));
	if(!pat || !pl) 
	{
		return;
	}
	for(i=0;i<pat->numtri;i++)
	{
		if(triSplitPoints(pair,pat->vlist,pat->tlist[i],pl))
			fifoPush(&pf,pair);
	}
	if(numpairs)
		*numpairs=fifoCount(&pf);
	if(Dest)
	{
#ifdef _MSC_VER
		/* (this for ViasualC 5.0 (opt ON :); env: NT 4.0 ) */
		__asm { nop } /* Please, DON'T REMOVE THIS */
#endif
		*Dest=(vec3_t*)memAlloc(2*sizeof(vec3_t)*fifoCount(&pf));
		i=0;
		while(fifoPop(&pf,pair))
		{
			vec3Copy((*Dest)[i++],pair[0]);
			vec3Copy((*Dest)[i++],pair[1]);
		}
	}
	fifoClear(&pf);
}

/*
	===============================
	uuRemoveDegenerateLines
	Убирает из набора пары с совпадающими точками. Распределяет новый
	блок, старый может удалить самостоятельно.
	Внимание ! Текущее значение величин XXX_EPSILON обеспечивает хорошее
	"сцепление" линий в парах и плохое удаление: эти функции создавались
	для построения картины изолиний.
	===============================
*/

int	V_LOCAL
uuRemoveDegenerateLines(vec3_t **dst,vec3_t *src,int *numlin,
	vboolean keepon)
{
	int i,j,newlin;
	int numlines=*numlin;
	vec3_t cur[2],nxt[2];
	vec3_t *temp1;
	vec3_t *temp2;
	if(!src || !numlin)
	{
		return 0;
	}
	temp1=(vec3_t*)memAlloc(2*numlines*sizeof(vec3_t));
	temp2=(vec3_t*)memAlloc(2*numlines*sizeof(vec3_t));
	newlin=0;
	for(i=0;i<numlines;i++)
	{
		if(vec3IsEqualEpsilon(src[i*2],src[i*2+1],DIFF_EPSILON))
			continue;
		vec3Copy(temp1[newlin*2],src[i*2]);
		vec3Copy(temp1[newlin*2+1],src[i*2+1]);
		++newlin;
	}
	numlines=0;
	/* следует учесть, что среди линий могут быть дубликаты */
	for(i=0;i<newlin;i++)
	{
		vec3Copy(cur[0],temp1[i*2]);
		vec3Copy(cur[1],temp1[i*2+1]);
		for(j=0;j<numlines;j++)
		{
			/*	ищем cur в массиве temp2 */
			/*	Да, я знаю, что первая итерация лишняя. Я устал от этого
			 *	кода, который отлаживаю неделю.
			 */
			vec3Copy(nxt[0],temp2[j*2]);
			vec3Copy(nxt[1],temp2[j*2+1]);
			if((vec3IsEqualEpsilon(cur[0],nxt[0],DIFF_EPSILON) &&
				vec3IsEqualEpsilon(cur[1],nxt[1],DIFF_EPSILON))||
				(vec3IsEqualEpsilon(cur[1],nxt[0],DIFF_EPSILON) &&
				vec3IsEqualEpsilon(cur[0],nxt[1],DIFF_EPSILON)))
				break;
		}
		if(j<numlines) /* dupe */
			continue;
		vec3Copy(temp2[numlines*2],cur[0]);
		vec3Copy(temp2[numlines*2+1],cur[1]);
		++numlines;
	}
	newlin=numlines;
	if(dst)
	{
		*dst=(vec3_t*)memAlloc(newlin*2*sizeof(vec3_t));
		memCopy(*dst,temp2,newlin*2*sizeof(vec3_t));
	}
	memFree(temp1);
	memFree(temp2);
	if(!keepon)
		memFree(src);
	*numlin=newlin;
	return newlin;
}

u32_t	poolsize = 0;
vec3_t	bigpool[1024 * 1024];

void contour_me(tripatch_p pat)
{
	plane3_t pl  = {{0,0,1},10};
	int i;
	vec_t step;
	vec3_t *rawlines,*lines;
	int numlines;
	char name[256];
	int zsplits = 11;
	step = 10;

	{
		vec_t zmin = 100000000;
		vec_t zmax = -100000000;
		
		int i;
		for(i=0;i<pat->numvert;i++)
		{
			vec_t z = pat->vlist[i][2];
			if(z > zmax)
				zmax = z;
			if(z < zmin)
				zmin = z;
		}
		
		printf("zmin %g\tzmax %g\n",zmin,zmax);
	}
		
	for(i=0;i<zsplits;i++)
	{
		pl.dist -= step;
		patIntersectWithPlane(&rawlines,&numlines,pat,&pl);
		if(!numlines)
		{
			printf("no splits with plane %d...\n",i);
			continue;
		}
		uuRemoveDegenerateLines(&lines,rawlines,&numlines,false);
		if(!numlines)
		{
			printf("all lines (plane %d) degenerate.\n",i);
			continue;
		}
		sprintf(name,"ContourLayer%02d.dxf",i);
		vhandle_t dxf = dxfOpenWrite(name);
		vec3_t *ptr = lines;
		for(int j=0;j<numlines;j++,ptr += 2)
		{
			dxfLine(dxf,ptr[0],ptr[1]);
			vec3Copy(bigpool[poolsize++],ptr[0]);
			vec3Copy(bigpool[poolsize++],ptr[1]);
		}
		dxfClose(dxf);
		memFree(lines);
	}
	vhandle_t dxf = dxfOpenWrite("AllLayers.dxf");
	vec3_t *ptr = bigpool;
	for(int j=0;j<poolsize;j++,ptr += 2)
		dxfLine(dxf,ptr[0],ptr[1]);
	dxfClose(dxf);
}

int grid_neural(nnwork &model)
{
	int split = 32;

	vec_t startang = - M_PI + M_PI/6;
	
	vec3_t center = {0,0,0};
	tripatch_p out;
	
	out = ppTriangulate2D(center,100,startang,split,false,
		patterns[0][0],patterns[0][1],
		patterns[1][0]-patterns[0][0],
		patterns[2][1]-patterns[0][1],
		nngridproc,model);

	patSaveDXF(out,"neural-response.dxf");


	patFree(out);

	split = 50;

	out = ppTriangulate2D(center,100,startang,split,false,
		patterns[0][0],patterns[0][1],
		patterns[1][0]-patterns[0][0],
		patterns[2][1]-patterns[0][1],
		nngridproc,model);

	contour_me(out);
	
	patFree(out);
	
	split = 3;
	
	base=0;	
	out = ppTriangulate2D(center,100,startang,split,false,0.4,0.5,0.3,0.3,procbase,model);
	patSaveDXF(out,"neural-baseplane-00.dxf");
	patFree(out);
	
	base=20;	
	out = ppTriangulate2D(center,100,startang,split,false,0.4,0.5,0.3,0.3,procbase,model);
	patSaveDXF(out,"neural-baseplane-20.dxf");
	patFree(out);
	

	base=40;	
	out = ppTriangulate2D(center,100,startang,split,false,0.4,0.5,0.3,0.3,procbase,model);
	patSaveDXF(out,"neural-baseplane-40.dxf");
	patFree(out);
	

	base=60;	
	out = ppTriangulate2D(center,100,startang,split,false,0.4,0.5,0.3,0.3,procbase,model);
	patSaveDXF(out,"neural-baseplane-60.dxf");
	patFree(out);
	
	base=80;	
	out = ppTriangulate2D(center,100,startang,split,false,0.4,0.5,0.3,0.3,procbase,model);
	patSaveDXF(out,"neural-baseplane-80.dxf");
	patFree(out);
	
	base=100;	
	out = ppTriangulate2D(center,100,startang,split,false,0.4,0.5,0.3,0.3,procbase,model);
	patSaveDXF(out,"neural-baseplane-100.dxf");
	patFree(out);
	

	split = 6;
	
	base=0;	
	out = ppTriangulate2D(center,100,startang,split,false,0.4,0.5,0.3,0.3,procbase,model);
	patSaveDXF(out,"baseplane-dense-00.dxf");
	patFree(out);

	split = 1;
	
	base=0;	
	out = ppTriangulate2D(center,100,startang,split,false,0.4,0.5,0.3,0.3,procbase,model);
	patSaveDXF(out,"baseplane-hollow-00.dxf");
	patFree(out);

	return 0;
}
