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

        Final Project                                 MMU 1996

        ######################################################
        #                                                    #
        #               A Modular Neural Network             #   
        #                                                    #
        #               THE BACKPROPAGATION CLASS            #   
        #                                                    #
        ######################################################

        Albrecht Schmidt                              09.08.96

        FILE: CBackPro.C                           Version 1.0

   ************************************************************* */
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "global.h"
#include "CSupport.h"
#include "CLayer.h"
#include "CBackPro.h"

#define PRG_NAME        "\nCBackPro"
#define VERSION         "Ver 3.0"


//#define DEBUG   1
// #define LOG_WRITE   1
//#define PRINT   1

/* ---------------------------------------------------------------------
	The Implementation of the constructor/destructor
-------------------------------------------------------------------- */
/* This is a Constructor:
   It is used to set up a network with the known parameters.
   The number of neurons in the last used layer is equal to
   the number of outputs.                                        */
CBackPro::CBackPro(int no_i, 		// no of inputs
		   int no_l, 		// no of layers
		   double la,		// lambda value
		   TFunction f,		// tranfer function
		   char *logfile,	// name of the log file
		   int neu0,		// no of neurons in layer 0
		   int neu1,		// no of neurons in layer 1
		   int neu2, int neu3,  // no of neurons in the following
		   int neu4, int neu5)  // layers (default = 0)
{
	#if DEBUG
		cout << PRG_NAME << "::CBackPro(...)  ARG Constructor";
	#endif

	// check is the no of layers is valid: 2 <= no_l < MAX_LAYER
	if (no_l < 2)
	{
		cout << ERR_MSG << " to few Layers (<2)!!!\n";
		cout << "\n Program terminated ;-( \n";
		return;
	}
	if (no_l >= MAX_LAYER)
	{
		cout << ERR_MSG << " to many Layers (>5)!!!\n";
		cout << "\n Program terminated ;-( \n";
		return;
	}

	// Set Random start value (time dependent)
	Randomize();

	// Store the parameters in the class variables
	no_inputs = no_i;
	no_layers = no_l;
	lambda = la;
	function = f;
	no_neurons[0] = neu0;
	no_neurons[1] = neu1;
	no_neurons[2] = neu2;
	no_neurons[3] = neu3;
	no_neurons[4] = neu4;
	no_neurons[5] = neu5;

	int i;

	// initialize the layers by calling the constructors in the class CLayer
	layer[0] = new CLayer(no_inputs, no_neurons[0], function, lambda);
	for(i=1;i<no_layers;i++)
	{
		layer[i] =   new CLayer(no_neurons[i-1],
					no_neurons[i],
					function, lambda);

	}

	// open the logfile if necessary and make the start entry
	screenStatus = ON;
	if (strcmp(logfile, NO_LOG) == 0)
        {
		logStatus = OFF;
	}
        else
        {
		logStatus = ON;
		logfd = fopen(logfile, "wt");
		if (logfd == NULL)
		{
	   	    cout << ERR_MSG << "can not open file :" << logfile << " !!!";
	   	    return;
		}
		fprintf(logfd, "\n This is the logfile of the BP program\n");
		fprintf(logfd, "\n Running Version: %s\n", VERSION);
		fprintf(logfd, "\n Network initialized randomly !!! \n");
		fflush(logfd);
	}
}

/* This is a Constructor:
   The first parameter is the name of file which contains a description
   of a network (with or without weights). The net description files
   might be type by using any editor or generated by the method
   CBackPro::StoreNet(...) . The second parameter is the
   name of the logfile.

								*/

CBackPro::CBackPro(char *filename, 	// net description file
		   char *logfile) 	// name of the logfile
{
	#if DEBUG
		cout << PRG_NAME << "::CBackPro(...)  File Constructor";
	#endif
	FILE *infile;
	char comment[MAX_SEP_LEN];
	int i, tmp;

	// Open the Network Desciption file
	infile = fopen(filename, "rt");
	if (infile == NULL)
	{
	      cout << ERR_MSG << "can not open file :" << filename << " !!!";
	      return;
	}


	// Set Random start value (time dependent)
	Randomize();



	// Read the class variables, read the comment but don't use it.
	fscanf(infile, "%i %s\n", &no_layers, comment);
	fscanf(infile, "%i %s\n", &no_inputs, comment);
	fscanf(infile, "%lf %s\n", &lambda, comment);
	fscanf(infile, "%i %s\n", &tmp, comment);

	// Convert Integer to TFuction
	if (tmp == 0)
		function = bi_pol; else function = uni_pol;
	// Read Number of Neurons
	for(i=0;i<no_layers;i++)
		fscanf( infile, "%i ", &no_neurons[i]);
	fscanf( infile, "%s\n", comment);

	// Create the layers
	// First layer has no_inputs Inputs and no_neurons[0] Neurons
	// The following layers have the nuber of neurons from the
	// previous layer as input.
	layer[0] = new CLayer(no_inputs, no_neurons[0], function, lambda);
	for(i=1;i<no_layers;i++)
	{
		layer[i] =   new CLayer(no_neurons[i-1],
					no_neurons[i],
					function, lambda);

	}

	// Read - and if weights in the file initialze the layers
	// with the weights else they are already randomly initialized
	for(i=0;i<no_layers;i++)
	{
		fscanf( infile, "%s\n" , comment);
		if (( comment[0] != '-' ) && (i==0))
		{
			cout << "\nNo or wrong weights !! \n";
			cout << "Initialize randomly !!!\n";
			i = no_layers;
		}
		else
		{
			if ( comment[0] != '-' )
			   cout << "\n wrong weight file !! \n";
			else
			   layer[i]->ReadWeights(infile);
		}
		fscanf( infile, "\n");
	}

	// close the description file
	fclose(infile);
	// open the logfile an make the start entry
	screenStatus = ON;


        // open the logfile if necessary and make the start entry
        if (strcmp(logfile, NO_LOG) == 0) 
        { 
                logStatus = OFF;
        } 
        else 
        { 
                logStatus = ON;  
                logfd = fopen(logfile, "wt");
                if (logfd == NULL)
                {
                    cout << ERR_MSG << "can not open file :" << logfile << " !!!";                    return;
                }  
                fprintf(logfd, "\n This is the logfile of the BP program\n");
                fprintf(logfd, "\n Running Version: %s\n", VERSION);
		fprintf(logfd, "\n Network initialized from file: %s !!! \n",
			  filename);
		fprintf(logfd, "\n Start Time: %s \n", Read_SysTime());
                fflush(logfd);
		fflush(logfd);
        }
}

// The destructor
CBackPro::~CBackPro()
{
	#if DEBUG
		cout << PRG_NAME << "::~CBackPro(...)  Destructor";
	#endif
	int i;
	// call the destructor in each layer
	for(i=0;i<no_layers;i++)
	{
		layer[i]->CLayer::~CLayer();
	}
	// make the log entries
        if (logStatus == ON)
        {
		fprintf(logfd, "\n Bye - Destructor called !!!\n");
		fprintf(logfd, "\n End   Time: %s \n", Read_SysTime());
		// close the log file
		fclose(logfd);
        }
}

/* ---------------------------------------------------------------------
		The Implementation of the further functions
-------------------------------------------------------------------- */
// Reset the weighst to a random value in a Range between min and max
void CBackPro::ResetWeights(double min, double max)
{
        #if DEBUG
                cout << PRG_NAME << "::ResetWeights(...)";
        #endif
        int i;
        // call the reset function in each layer
        for(i=0;i<no_layers;i++) 
        {
                layer[i]->CLayer::ResetWeights(min, max);
        }
        // make the log entries
	if (logStatus == ON)
	{
	  fprintf(logfd, "\n Reseted the weights !!!\n");
	  fprintf(logfd, "\n At Time: %s \n", Read_SysTime());
	  fflush(logfd);
	}
}

// Store the network in a file
void CBackPro::StoreNet(char *filename)
{
	#if DEBUG
		cout << PRG_NAME << "::StoreNet(...)";
	#endif

	FILE *outfile;
	int i;

	// make the log entries
	if (logStatus == ON)
	{
	  fprintf(logfd, "\n Write the Network to file %s", filename);
	  fprintf(logfd, " at %s .\n", Read_SysTime());
	  fflush(logfd);
	}

	// open the output file to write
	outfile = fopen(filename, "wt");
	if (outfile == NULL)
	{
	      cout << ERR_MSG << "can not open file :" << filename << " !!!";
	      return;
	}

	// write the calss variables
	fprintf(outfile, "%i :no_layers\n", no_layers);
	fprintf(outfile, "%i :no_of_inputs\n", no_inputs);
	fprintf(outfile, "%f :lambda\n", lambda);
	fprintf(outfile, "%i :function\n", function);

	// write the nuber of neurons in each layer
	for(i=0;i<no_layers;i++)
		fprintf( outfile, "%i ", no_neurons[i]);
	fprintf( outfile, ":no_neurons_per_layer\n");

	// ask the layers to store their weights
	for(i=0;i<no_layers;i++)
	{
		fprintf( outfile, "-----------------\n");
		layer[i]->WriteWeights(outfile);
		fprintf( outfile, "\n");
	}

	fprintf( outfile, "END." );
	// close the ouput file
	fclose(outfile);
}

// calculate the output for a given input
TVector CBackPro::Apply(TVector outV, TVector inpV)
{
	#if DEBUG
		cout << PRG_NAME << "::Apply(...)  ";
	#endif

	TVector out[MAX_LAYER];

	int i;
	// allocate memory for the output values in each layer
	for(i=0;i<no_layers-1;i++)
	{
		out[i] = new double[no_neurons[i]];
	}

	// forward calculation
	// First layer is supplied with the input Vector
	out[0] = layer[0]->Out(out[0], inpV);
	// the following layers have the output from the
	// privious layer as input
	for(i=1;i<no_layers-1; i++)
	{
		// ask the layers to calculate the output Vector
		out[i] = layer[i]->Out(out[i], out[i-1]);
	}
	// call the ouput layer with the variable (outV) with is
	// supplied (and allocate) from the caller of Apply
	outV = layer[no_layers-1]->Out(outV, out[no_layers-2]);

	// free the memory
	for(i=0;i<no_layers-1;i++)
	{
		delete[] out[i];
	}
	// return the result vector
	return outV;
}

// Learn one vector for which the result is known
double CBackPro::Learn(TVector inpV,        // the vector to learn
		      TVector tarV,        // the desired result
		      double eta,           // the learning constant
		      double alpha)         // the momentum
{
	#if DEBUG
		cout << PRG_NAME << "::Learn(...)  ";
	#endif
	int i;
	double error =0;

	TVector net[MAX_LAYER], out[MAX_LAYER], delta[MAX_LAYER];

	// allocation of memory for the intermediate results
	for(i=0;i<no_layers;i++)
	{
		net[i] = new double[no_neurons[i]];
		out[i] = new double[no_neurons[i]];
		delta[i] = new double[no_neurons[i]];
	}

	// forward calculation of net an output values
	net[0] = layer[0]->Net(net[0], inpV);
	out[0] = layer[0]->Out(out[0], inpV);
	for(i=1;i<no_layers; i++)
	{
		net[i] = layer[i]->Net(net[i], out[i-1]);
		out[i] = layer[i]->Out(out[i], out[i-1]);
	}

	// calculation of th error signal
	// for the oupput layer
	delta[no_layers-1] = layer[no_layers-1]->DeltaOut(delta[no_layers-1],
							  out[no_layers-2],
							  tarV);
	// for the hidden layers
	for(i=(no_layers-2);i>=0;i--)
	{
		delta[i] = layer[i+1]->DeltaHidden(delta[i], net[i], delta[i+1]);
	}

	// backpropagation of the error and update of the weights
	layer[0]->UpdateWeights(eta, inpV, delta[0], alpha);
	for(i=1;i<no_layers; i++)
	{
		layer[i]->UpdateWeights(eta, out[i-1], delta[i], alpha);
	}

	#if LOG_WRITE   // Only for DEBUG !!!!!
		fprintf(logfd, "\nLearn:\n");
		fprintf(logfd, "\ninpV: ");
		WriteVector(inpV, no_inputs, logfd);
		fprintf(logfd, "\ntarV: ");
		WriteVector(tarV, no_neurons[no_layers-1], logfd);
		for(i=0;i<no_layers;i++)
		{
			fprintf(logfd, "\n net[%i] ", i);
			WriteVector(net[i], no_neurons[i], logfd);
			fprintf(logfd, "\n out[%i] ", i);
			WriteVector(out[i], no_neurons[i], logfd);
			fprintf(logfd, "\n delta[%i] ", i);
			WriteVector(delta[i], no_neurons[i], logfd);
		}
		fflush(logfd);
	#endif

	// calculate the error
	//                                   2
	// error = sum( (desired[i] - out [i]) )
	//          i

	for(i=0;i<no_neurons[no_layers-1];i++)
	{
		error = error + pow(tarV[i] - out[no_layers-1][i], 2);
	}

	// only for DEBUG
	// PrintV(tarV, no_neurons[no_layers-1], "tar-inBPL");
	// PrintV(out[no_layers-1], no_neurons[no_layers-1], "out-inBPL");
	// cout << "\n err in BP: " << error;

	// free the used memory
	for(i=0;i<no_layers;i++)
	{
		delete[] net[i];
		delete[] out[i];
		delete[] delta[i];
	}

	// return the error after this epoch
	return error;
}

// train the network on a file
double CBackPro::Train(double eta,	// the learning constant
		      double maxError,	// the accepted maximal error
		      char * filename,	// the file to learn
		      int steps,	// the maximal number of steps
		      char *errfile,	// the name of the error file
		      double alpha)	// the momentum
{
	#if DEBUG
		cout << PRG_NAME << "::Train(...)";
	#endif

	if (logStatus == ON)
	{
	  // write the log entries
	  fprintf(logfd, "\n Learn the file %s", filename);
	  fprintf(logfd, "\n Learning Constant eta : %f", eta);
	  fprintf(logfd, "\n Max Error             : %f", maxError);
	  fprintf(logfd, "\n Momentum alpha        : %f", alpha);
	  fprintf(logfd, "\n Max Number of steps   : %i", steps);
	  fprintf(logfd, "\n Write error in file   : %s", errfile);
	  fprintf(logfd, "\n Start learning at     : %s", Read_SysTime());
	  fflush(logfd);
	}
	TinV_outV trainingsSet[MAX_TRAIN];

	// the file descriptors
	FILE *fd,       // the input file
	     *errfd;    // the error file
	char sep1[20], sep2[20];  // the separators in the data file
	double error;    // root mean square error
	int *mix;       // permuation array
	int i,j,
	    count = 0,  // number of trainings records
	    endLoop = FALSE;

	// open the error file
	errfd= fopen(errfile, "wt");
	if (errfd == NULL)
	{
	   cout << ERR_MSG << "can not open file :" << errfile << " !!!";
	   return -1;
	}

	// open the input file
	fd = fopen(filename, "rt");
	if (fd == NULL)
	{
	   cout << ERR_MSG << "can not open file :" << filename << " !!!";
	   return -1;
	}

	// read the traingsdata and allocate memory for it
	while ( !feof(fd)  && (endLoop == FALSE) && (count < MAX_TRAIN))
	{
	   // allocate
	   trainingsSet[count].inputV = new double[no_inputs+1];
	   trainingsSet[count].targetV = new double[no_neurons[no_layers-1]];

	   // read the input vector
	   trainingsSet[count].inputV = ReadVector(trainingsSet[count].inputV,
						   no_inputs, fd);
	   // read the separartor ":"
	   fscanf(fd, "%1s", sep1);

	   // read the desired vector
	   trainingsSet[count].targetV = ReadVector(trainingsSet[count].targetV,
						    no_neurons[no_layers-1], fd);

	   // read the separartor ";" or "."
	   fscanf(fd, "%1s", sep2);

	   #ifdef PRINT
	      PrintV(trainingsSet[count].inputV, no_inputs, "inV");
	      PrintV(trainingsSet[count].targetV, no_neurons[no_layers-1], "outV");
	      cout << "\n sep1: " << sep1 << "   sep2: " << sep2 << "\n";
	   #endif


	   //check if the file is in the last line
	   // if yes => stop reading
	   if (sep1[0] == ':' &&  sep2[0] == '.' )
	   {
		endLoop = TRUE;
	   }

	   // check if the file is ok
	   if (sep1[0] != ':' ||  ( sep2[0] != ';'  &&  sep2[0] != '.' ))
	   {
	     FileWarning(filename);
	     return -1;
	   }

	   // increase the number of records
	   count++;
	}

	// close the input file
	fclose(fd);

	// alloacte the memory for the permuatation array
	mix = new int[count];
	// fill the array in order
	for(i=0; i<count; i++) mix[i] = i;

	// learn the data, data is randomly selected
	for(i=0;i<steps;i++)
	{
	   error = 0;
	   // make a permutation
	   mix = Mix(mix, count, count);
	   // learn all trainings records
	   for(j=0;j<count;j++)
	   {
		#if PRINT
		  cout << "\n mix[" << j << "] = " << mix[j] << "\n";
		  cout << "Learn the following Vectors:";
		  PrintV(trainingsSet[mix[j]].inputV, no_inputs, "inV");
		  PrintV( trainingsSet[mix[j]].targetV,
			  no_neurons[no_layers-1], "outV");
		#endif

		 // learn the vector and get the error as result back
		error += Learn( trainingsSet[mix[j]].inputV,
				trainingsSet[mix[j]].targetV,
				eta, alpha);
	    }
	    #if DEBUG
		cout << "\n error abs: " << error;
	    #endif

	    // calculate the root-mean square error
	    error = (1.0/ (count * (no_neurons[no_layers-1]))) * sqrt(error);

	    // write log entriy
	    fprintf(errfd, "%i %f\n", i, error);
	    fflush(errfd);

	    // check if the end condition is reached
	    if (error <= maxError) break;
	}

	if (logStatus == ON)
	{
	  // write log entriy
	  fprintf(logfd, "\n End learning at        : %s\n", Read_SysTime());
	  fflush(logfd);
	}

	// close error file
	fclose(errfd);

	// give the memory free
	for(j=0;j<count;j++)
	{
	   delete[] trainingsSet[j].inputV;
	   delete[] trainingsSet[j].targetV;
	}

	// returnn the root mean square error
	return error;
}


// calculate for an input file the results
void CBackPro::Work(char * infile,    // input file with input data
		    char *outfile)    // the result file
{
	#if DEBUG
		cout << PRG_NAME << "::Work(...)";
	#endif

	// the file descriptors
	FILE *infd, *outfd;

	char sep[20]; // the separator
	TVector inV, outV;

	// allocate memory for the calcualtion
	inV = new double[no_inputs];
	outV = new double[no_neurons[no_layers-1]];
	int endLoop = FALSE;

	if (logStatus == ON)
	{
	  // write log entries
	  fprintf(logfd, "\n Work on file     : %s", infile);
	  fprintf(logfd, "\n Result on        : %s", outfile);
	  fprintf(logfd, "\n Start working at : %s", Read_SysTime());
	  fflush(logfd);
	}

	// open output file
	outfd= fopen(outfile, "wt");
	if (outfd == NULL)
	{
	   cout << ERR_MSG << "can not open file :" << outfile << " !!!";
	   return;
	}

	// open input file
	infd = fopen(infile, "rt");
	if (infd == NULL)
	{
	   cout << ERR_MSG << "can not open file :" << infile << " !!!";
	   return;
	}

	// for all input vector in the infile do
	while ( !feof(infd)  && (endLoop == FALSE))
	{
	   // Read a Vector
	   inV  = ReadVector(inV, no_inputs, infd);

	   // read the separator
	   fscanf(infd, "%1s", sep);
	   // check if end of data is reached
	   if (sep[0] == '.' )
	   {
		endLoop = TRUE;
	   }

	   // check if file is correct
	   if ( sep[0] != ';'  &&  sep[0] != '.' )
	   {
	     FileWarning(infile);
	     return;
	   }

	   // caluculate the output vector
	   outV = Apply(outV, inV);

	   // write the result in the output file
	   WriteVector(inV, no_inputs, outfd);
	   fprintf(outfd, " : ");
	   WriteVector(outV, no_neurons[no_layers-1], outfd);
	   if (endLoop == TRUE)
		 fprintf(outfd, " . ");
	   else
		 fprintf(outfd, " ; \n");

	}

	// close the files
	fclose(infd);
	fclose(outfd);

	if (logStatus == ON)
	{
	  // write log entry
	  fprintf(logfd, "\n End working at        : %s\n", Read_SysTime());
	  fflush(logfd);
	}

	// give memory free
	delete[] inV;
	delete[] outV;
}


// Test the performance on a data file (with input and desired
// values)
double CBackPro::Test( char *testfile,         // the data file
		      double max_difference )  // the difference between
					      // desired and output
{
	#if DEBUG
		cout << PRG_NAME << "::Test(...)";
	#endif

	if (logStatus == ON)
	{
	  // write log enrties
	  fprintf(logfd, "\n Test on file     : %s", testfile);
	  fprintf(logfd, "\n Max difference   : %f", max_difference);
	  fprintf(logfd, "\n Start testing at : %s", Read_SysTime());
	  fflush(logfd);
	}

	double result;  // the performance
	TVector outV;
	TinV_outV testV;

	FILE *fd;      // the data file
	char sep1[20], sep2[20]; //separators
	int i,
	    good = 0,  // number of recognized values
	    bad = 0,   // number of not recognized values
	    count = 0, // number of output values
	    endLoop = FALSE;

	// allocate memory
	testV.inputV = new double[no_inputs+1];
	testV.targetV = new double[no_neurons[no_layers-1]];
	outV = new double[no_neurons[no_layers-1]];

	// open the data file
	fd = fopen(testfile, "rt");
	if (fd == NULL)
	{
	   cout << ERR_MSG << "can not open file :" << testfile << " !!!";
	   return 1;
	}

	// check if difference is in a useful range
	switch( function )
	{
	  case uni_pol:
		{
		  if (max_difference > 0.5  || max_difference <0)
		  {
			cout << WARN_MSG << " wrong maximal difference !!!";
			cout << "\n set max differnce to 0.5 !!!\n";
			max_difference = 0.5;
		  }
		  break;
		}
	  case bi_pol:
		{
		  if (max_difference > 1  || max_difference <0)
		  {
			cout << WARN_MSG << " wrong maximal difference !!!";
			cout << "\n set max differnce to 1 !!!\n";
			max_difference = 1;
		  }
		  break;
		}
	}


	// read the traingsdata and allocate memory for it
	while ( !feof(fd)  && (endLoop == FALSE) )
	{
	   // read input vector
	   testV.inputV = ReadVector(testV.inputV,
						   no_inputs, fd);
	   // read separator ":"
	   fscanf(fd, "%1s", sep1);

	   // read output vector
	   testV.targetV = ReadVector(testV.targetV,
				      no_neurons[no_layers-1], fd);

	   // read separator ";" or "."
	   fscanf(fd, "%1s", sep2);

	   #ifdef PRINT
	      PrintV(testV.inputV, no_inputs, "inV");
	      PrintV(testV.targetV, no_neurons[no_layers-1], "outV");
	      cout << "\n sep1: " << sep1 << "   sep2: " << sep2 << "\n";
	   #endif

	   // check if the file is terminated
	   if (sep1[0] == ':' &&  sep2[0] == '.' )
	   {
		endLoop = TRUE;
	   }

	   // check if the file is correct
	   if (sep1[0] != ':' ||  ( sep2[0] != ';'  &&  sep2[0] != '.' ))
	   {
	     FileWarning(testfile);
	     return 1;
	   }

	   // calculate the ouput vector
	   outV = Apply(outV, testV.inputV);

	   // only for experiment !!!
    	   //PrintV(testV.targetV, no_neurons[no_layers-1] , "desired");
    	   //PrintV(outV, no_neurons[no_layers-1], "result");


	   // compare it (each componet)
	   for(i=0;i<no_neurons[no_layers-1];i++)
	   {
		if ( fabs(outV[i] - testV.targetV[i]) < max_difference )
			good++;
		else
			bad++;
	   }

	   count++;
	}

	// close the data file
	fclose(fd);

	// calculate the performance in percent
	// for componets
	result = (good * 100.0) / (double)(count*no_neurons[no_layers-1]) ;
	


	if (logStatus == ON)
	{
	  // write the log file
	  fprintf(logfd, "\n Statistic on this test:");
	  fprintf(logfd, "\n Number of values: %i",
			  count*no_neurons[no_layers-1]);
	  fprintf(logfd, "\n recognized      : %i", good);
	  fprintf(logfd, "\n not recognized  : %i", bad);
	  fprintf(logfd, "\n Performance     : %f %%", result);
	  fprintf(logfd, "\n End testing at        : %s .\n", Read_SysTime());
	  fflush(logfd);
	}

	if (screenStatus == ON)
	{
	  // print results on the screen
	  cout << "\n\nStatistics: no of values: " <<
		   count*no_neurons[no_layers-1];
	  cout <<   "\n            no of goods : " << good;
	  cout <<   "\n            no of bads  : " << bad;
	  cout << "\n\n            recognition : " << result << "%\n";
  	  char dummy[MAX_STR_LEN];
       	  cout << "\n\nPress >RETURN< for the menu!\n";
       	  gets(dummy);
	}

	// free the memory
	delete[] testV.inputV;
	delete[] testV.targetV;
	delete[] outV;

	// return the performance
	return result;
}

// functions to activate the writing of the Logfile
void CBackPro::LogWriteOn()
{
	#if DEBUG
		cout << PRG_NAME << "::LogWriteOn()";
	#endif

        if (logfd == NULL)
        {
              cout << WARN_MSG << "There is no Logfile !!!" ; 
              logStatus = OFF;
                
        }
        else
        {
		logStatus = ON;
		// write log enrties
		fprintf(logfd, "\n Activate writing of Logfile !" );
		fflush(logfd);
	}
}



// functions to deactivate the writing of the Logfile
void CBackPro::LogWriteOff()
{
	#if DEBUG
		cout << PRG_NAME << "::LogWriteOff()";
	#endif

        if (logStatus == ON)
        {
		// write log enrties
		fprintf(logfd, "\n Deactivate writing of Logfile !" );
		fflush(logfd);
        }
	logStatus = OFF;
}

// functions to activate the writing on the screen
void CBackPro::ScreenOn()
{
	#if DEBUG
		cout << PRG_NAME << "::ScreenOn()";
	#endif

	screenStatus = ON;


}



// functions to deactivate the writing on the screen
void CBackPro::ScreenOff()
{
	#if DEBUG
		cout << PRG_NAME << "::ScreenOff()";
	#endif

	screenStatus = OFF;

}
