/* *************************************************************
 
        Final Project                                 MMU 1996

        ######################################################
        #                                                    #
        #               A Modular Neural Network             #   
        #                                                    #
        #       A SINGLE LAYER OF THE MODULAR NETWORK        #   
        #                                                    #
        ######################################################
 
        Albrecht Schmidt                              09.08.96
 
        FILE: CMLayer.C                             Version 1.0
 
   ************************************************************* */
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "CData.h"
#include "neuro.h"
#include "CMLayer.h"


//#define DP(X) cout << X ;
#define DP(X) ;

// the constructor with parameters
CMulLayer::CMulLayer(int no_sub_nets,
		     int no_u_inputs,
                     TNetDef *net_def,
		     int r_seed )
{
        DP("\nCMulLayer::CMulLayer(" << no_sub_nets 
	                     << ", " << net_def->no_inputs
	                     << ", " << net_def->no_layers
	                     << ", " << net_def->lambda
	                     << ", " << net_def->fct
	                     << ", " << net_def->neuronL[0]
	                     << ", " << net_def->neuronL[1]
	                     << ", " << net_def->neuronL[2]
	                     << ", " << net_def->neuronL[3]
	                     << ", " << net_def->neuronL[4]
	                     << ", " << net_def->neuronL[5]<< ")" );


	no_nets = no_sub_nets;
	no_inputs_net = net_def->no_inputs;
    	no_inputs_user = no_u_inputs;
    	no_inputs = no_nets * no_inputs_net;
	rnd_seed = r_seed;
	if(no_inputs_user > no_inputs)
        {
              cout << WARN_MSG << "Network to small for all inputs !!!" ;
	}

        no_outputs_net = net_def->neuronL[net_def->no_layers-1];	

	DP("\n No_nets: " << no_nets <<
           "\n No_inps_per_net: " << no_inputs_net <<
           "\n No_user_inputs: " << no_inputs_user <<
           "\n No_input: " << no_inputs <<
	   "\n No_output: " << no_outputs_net << ")" );

        net_definition = new TNetDef;
	ext_inputV = new double[no_inputs];
	mapped_inputV = new double[no_inputs];

	inputMapping = new int[no_inputs];
        int i;
        for(i=0; i<no_inputs; i++) inputMapping[i] = i;
	if (rnd_seed != NO_RND_MAPPING)
	{
       	 	srand((long)rnd_seed);
        	inputMapping = Mix(inputMapping, no_inputs, no_inputs);
	}
		
        net_definition->no_inputs = net_def->no_inputs;
        net_definition->no_layers = net_def->no_layers;
        net_definition->lambda = net_def->lambda;
        net_definition->fct = net_def->fct;
	for(i=0; i<6; i++)
             net_definition->neuronL[i] = net_def->neuronL[i];

 
        for(i=0;i<no_nets;i++)
        {
                net[i] = new CBackPro(no_inputs_net,
                                      net_definition->no_layers,
                                      net_definition->lambda,
                                      net_definition->fct,
                                      NO_LOG,
                                      net_definition->neuronL[0],
				      net_definition->neuronL[1],
                                      net_definition->neuronL[2],
                                      net_definition->neuronL[3],
                                      net_definition->neuronL[4],
                                      net_definition->neuronL[5]);
        }
}


// the constructor from file
CMulLayer::CMulLayer(char *file_start)
{
	DP("\nCMulLayer::CMulLayer(" << file_start << ")" );

        FILE *infile;
        char comment[MAX_SEP_LEN], filename[MAX_SEP_LEN];
        int i;


        // Open the Network Desciption file
        infile = fopen(file_start, "rt");
        if (infile == NULL)
        {
              cout << ERR_MSG << "can not open file :" << file_start << " !!!";
              return;
        }
 
         // Read the class variables, read the comment but don't use it.
        fscanf(infile, "%i %s\n", &no_nets, comment);
        fscanf(infile, "%i %s\n", &no_inputs_net, comment);
        fscanf(infile, "%i %s\n", &no_inputs_user, comment);
        fscanf(infile, "%i %s\n", &no_inputs, comment);
        fscanf(infile, "%i %s\n", &no_outputs_net, comment);
        fscanf(infile, "%i %s\n", &rnd_seed, comment);


        DP("\n No_nets: " << no_nets <<
           "\n No_inps_per_net: " << no_inputs_net <<
           "\n No_input: " << no_inputs <<
           "\n Rnd_seed: " << rnd_seed <<
	   "\n No_output: " << no_outputs_net << ")" );

	fscanf(infile, "%s\n", comment);
	DP("\n comment: " << comment);
     
	for(i=0;i<no_nets;i++)
 	{
		fscanf(infile, "%s\n", filename);
		DP("\n read file: " << filename);
		net[i] = new CBackPro(filename, NO_LOG);
	} 
	fclose(infile);

        net_definition = new TNetDef;
	ext_inputV = new double[no_inputs];
	mapped_inputV = new double[no_inputs];

        inputMapping = new int[no_inputs];
        for(i=0; i<no_inputs; i++) inputMapping[i] = i;
        if (rnd_seed != NO_RND_MAPPING)
        {
                srand((long)rnd_seed);
                inputMapping = Mix(inputMapping, no_inputs, no_inputs);
        }
 
 } 


// the destructor
CMulLayer::~CMulLayer()
{
        DP("\nCMulLayer::~CMulLayer()" );
 
        int i;
        for(i=0;i<no_nets;i++)
        {
                net[i]->CBackPro::~CBackPro();
        }

	delete[] ext_inputV;
	delete[] mapped_inputV;
	delete[] net_definition;
	delete[] inputMapping;
}


// learning a vector of that the output is known
double CMulLayer::Learn(TVector inV, TVector tarV,
			double eta, double alpha )
{
	DP("\nCMulLayer::Learn(" << inV << ", " <<  tarV << ", "
				<<  eta  << ", " << alpha << ")" );

	ext_inputV    = extentVector(ext_inputV, no_inputs, 
			             inV, no_inputs_user);
	mapped_inputV = randVector(mapped_inputV, ext_inputV, 
				   no_inputs, inputMapping);

        // for DEBUG
	//PrintV(inV, no_inputs_user, "inV");
	//PrintV(tarV, no_outputs_net, "tarV");
	//PrintV(ext_inputV, no_inputs, "ext");
	//PrintV(mapped_inputV, no_inputs, "mapped");


        int i;
	double err = 0;
        for(i=0;i<no_nets;i++)
        {
                // for DEBUG
                // PrintV(mapped_inputV + (i* no_inputs_net),
                //        no_inputs_net, "Learn");

                err += net[i]->Learn(mapped_inputV + (i* no_inputs_net), 
			             tarV, eta, alpha);
        }

	DP( "Err: " << err );
        err = err / (double) no_nets;
        DP ( "Norm Err: " << err);

	return err;
} 



// calculate the response of the layer for an input
TVector CMulLayer::Apply(TVector resV, TVector inV) 
{
        DP("\nCMulLayer::Apply(" << resV << ", " <<  inV << ")" ); 
 
        ext_inputV    = extentVector(ext_inputV, no_inputs, 
                                     inV, no_inputs_user);
        mapped_inputV = randVector(mapped_inputV, ext_inputV, 
                                   no_inputs, inputMapping);
 
        // for DEBUG
	//PrintV(inV, no_inputs_user, "inV");
        //PrintV(ext_inputV, no_inputs, "ext");
        //PrintV(mapped_inputV, no_inputs, "mapped");
 
        double *tempV = new double[no_outputs_net];

	int i, j;
        for(i=0;i<no_nets;i++)
        {
                // for DEBUG
                //PrintV(mapped_inputV + (i* no_inputs_net),
                //       no_inputs_net, "Apply");

                tempV =  net[i]->Apply( tempV, 
				        mapped_inputV + (i* no_inputs_net));
 
                for(j=0;j<no_outputs_net;j++)
                {
                      resV[i*no_outputs_net + j] = tempV[j];
                }
        }
	delete[] tempV;

        // for DEBUG
	//PrintV(resV, no_outputs_net * no_nets, "resV");
	return resV;
} 

// routine to reset the weights of the Layer
void CMulLayer::ResetWeights(double min, double max)
{
        DP("\nCMulLayer::ResetWeights(" << min << ", " << max << ")" );

	int i;
        for(i=0;i<no_nets;i++)
        {
             net[i]->ResetWeights(min, max);
        }
}

// store the layer onto disk
void CMulLayer::Store(char *file_start) 
{
        DP("\nCMulLayer::Store(" << file_start << ")" );

	char *subfile = new char[MAX_SEP_LEN];
        FILE *outfile;
        int i;

	// UNIX subfile = sprintf(subfile, "%s.%i", file_start, i);
	// PC
	sprintf(subfile, "%s.inp", file_start);
	DP("\nWrite the input layer net in file: >>" << subfile << "<<" );

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

	 // Write the class variables, write the comment
	fprintf(outfile, "%i :no_of_nets\n", no_nets);
	fprintf(outfile, "%i :no_of_inputs_p_net\n", no_inputs_net);
	fprintf(outfile, "%i :no_of_inputs_user\n", no_inputs_user);
	fprintf(outfile, "%i :no_inputs_p_layer\n", no_inputs);
	fprintf(outfile, "%i :no_outputs_p_net\n", no_outputs_net);
	fprintf(outfile, "%i :rnd_seed\n", rnd_seed);

	fprintf(outfile, "-----the_net_descrition_files----\n");

	for(i=0;i<no_nets;i++)
	{
	     // UNIX subfile = sprintf(subfile, "%s.%i", file_start, i);
	     // PC
	     sprintf(subfile, "%s.%i", file_start, i);
	     DP("\nWrite the net " << i << " in file: >>" << subfile << "<<" );
	     fprintf(outfile, "%s\n", subfile);
	     net[i]->StoreNet(subfile);
	}
	delete[] subfile;

	fclose(outfile);
}
