
/* Generated by Interface Builder */

#import "NeuronCntrl.h"
#import "NeuronView.h"			// to access the NeuronView methods
#import <appkit/Application.h>
#import <streams/streams.h>
#import <objc/NXStringTable.h>
#import <sys/file.h>
#import <appkit/Panel.h>
#import <appkit/OpenPanel.h>
#import <appkit/SavePanel.h>
#import <math.h>			// for the sqrt function call

#define MAX_IN_WINDOW 9 // used for specifying how
			// many in NNV window before resizing scrollview.
			 
// GLOBAL VARIABLES
NXStream	*inputstream,	// input file stream
		*outputstream;	// output file stream

float *NeuronValues;	// values after calculation.
float **Network;	// network matrix Network[][]
NXPoint *NeuronLoc; 	// array of neuron locations for displaying.	

int NUM_INPUTS = 0;			// network info derived from .network
int NUM_NODES = 0;
int NUM_OUTPUTS = 0;
BOOL INPUT_OPENED = FALSE;		// boolean file open variables
BOOL NETWORK_OPENED = FALSE;
BOOL TEMPCLEAR = TRUE;			// boolean output buffer clear variable


@implementation NeuronCntrl

// method called when application starts (init)
- appDidInit:sender
{
  [super init];
  // get instances of support objects
  openReq = [OpenPanel new];
  writeReq = [SavePanel new];

  return self;
}


// opens specified input file and stores 1st input set in NeuronValues[]
- openInputFile:sender
{
    const char *fileName;
    const char *const types[2] = {"input",NULL};
    
    if (NETWORK_OPENED) // only open if .network already openned.
    {
      if ([openReq runModalForTypes:types] && (fileName = [openReq filename]))
      {	
	inputstream = NXMapFile(fileName, NX_READONLY);	// open the file stream
	INPUT_OPENED = TRUE;		// set the boolean flag
	[self nextInputSet:sender];	// read in first input set
      }
      else		// error or <cancel> occurred
    	NXRunAlertPanel(NULL,"Error On Open Request!", NULL, NULL, NULL);
    }
    else
      NXRunAlertPanel(NULL,
      "Must Open Network file first!", NULL, NULL, NULL);
      
    return self;
}


// open network, read size, allocate memory, load Network matrix, count inputs, // outputs, resize view according to NUM_NODES, display network.
// Then close file...
- openNetworkFile:sender
{
    // used for resizable scrollview...
    float viewArea,viewRatio;
    NXRect theViewRect;
    NXSize newView;
    // others used in this method...
    int i,j;
    NXStream		*networkstream;	// network file stream
   
    const char *fileName;
    const char *const types[2] = {"network",NULL};
    
    // initialize global variables
    NUM_INPUTS = 0;
    NUM_OUTPUTS =0;
    NUM_NODES = 0;
    
    // attempt to open .network file
    if ([openReq runModalForTypes:types] && (fileName = [openReq filename]))
    {
	networkstream = NXMapFile(fileName, NX_READONLY); // open the stream
	
	if (NETWORK_OPENED) // open a different network? (one already openned?)
	{
	  // first free previous memory...
	  for(i=0; i < NUM_NODES; i++)
	    free(Network[i]);
	  free(NeuronValues);
	  free(NeuronLoc);
	}
	NETWORK_OPENED = TRUE;		// set boolean flag
	NXScanf(networkstream,"%d\n",&NUM_NODES); // read in number of neurons.
	
	// allocate memory and initialize to 0's (done by calloc).
	if( (NeuronValues = (float *) calloc(NUM_NODES,sizeof(float))) == NULL)
	  exit(308);
	if( (NeuronLoc =(NXPoint *) calloc(NUM_NODES,sizeof(NXPoint))) == NULL)
	  exit(308);
        if( (Network = (float **) calloc(NUM_NODES,sizeof(float *)))
	   == NULL)
	{
	  printf("error callocing Network memory...!\n");
	  exit(308);	   
	}
	for(i=0; i < NUM_NODES; i++)
	  if( (Network[i] = (float *) calloc(NUM_NODES,sizeof(float))) == NULL)
	    exit(308);
	
	
	i= j =0; // initialize index's
	while (!NXAtEOS(networkstream))	// while not at end of stream.
	{
	  // scan in a row from .network file and store in network memory...
	  while (((NXGetc(networkstream)) !='\n') && (!NXAtEOS(networkstream)))
	    {
	        NXUngetc(networkstream);
	        NXScanf(networkstream,"%f",&Network[i][j]);
	        j++;
	    }
	    i++;
	    j=0;
	}
	
	// count inputs / outputs according to rows/cols of zeros.
	[self getNumOutputs];		// count output neurons.
	[self getNumInputs];		// count input neurons.
	
	// compute newsize for NeuronView according to number of neurons...       
	[[theNeuronView superview]getFrame:&theViewRect];  // retrieve view sz.
	viewArea = (theViewRect.size.width * theViewRect.size.height);
	viewRatio = (theViewRect.size.width / theViewRect.size.height);
	viewArea = viewArea*NUM_NODES/MAX_IN_WINDOW;  // calc newarea.
	newView.height = sqrt(viewArea/viewRatio);
	newView.width = newView.height*viewRatio;
	// resize theNeuronView
	[theNeuronView sizeTo:newView.width :newView.height];			     
	[theNeuronView display];	// call the methods to redraw
	NXCloseMemory(networkstream, NX_FREEBUFFER);  // close networkstream...
    }
    else
    	NXRunAlertPanel(NULL,"Error On Open Request!", NULL, NULL, NULL);
 
    return self;
}


// the method saves the outputstream to a file
// - either appending to old file
// - saving to a new file.
// - closing the file after and clearing the output buffer
- writeOutput:sender
{
    const char *name;

    if ((!TEMPCLEAR) && [writeReq runModal] && (name = [writeReq filename]))
    {
     
     NXSaveToFile(outputstream, name);
     NXCloseMemory(outputstream, NX_FREEBUFFER);
     TEMPCLEAR = TRUE;
    }
    else
       NXRunAlertPanel(NULL,"Error On Write Output Request!",NULL,NULL,NULL);

    return self;
}

// clear the outputstream (if not already clear) - start with a clear one.
- clearTemp:sender
{
     if (!TEMPCLEAR)
     {
       NXCloseMemory(outputstream, NX_FREEBUFFER);
       TEMPCLEAR = TRUE;
     }
     return self;
}


// read next input set from input file into NeuronValue[]
- nextInputSet:sender
{
    int i, numread, endFileChoice;
    
    // initialize NeuronValues[]
    for (i=0; i < NUM_NODES; i++)
      NeuronValues[i] = 0.0;
    i=0;
    // if both .input & .network files are open...
    if ( (INPUT_OPENED) && (NETWORK_OPENED) )	
    {
      // read in the next input set (next row)
      while ( (NXGetc(inputstream) != '\n') && (!NXAtEOS(inputstream)) )
      {
	NXUngetc(inputstream);
	NXScanf(inputstream,"%f",&NeuronValues[i]);
	i++;
      }
      numread = i;
      
      if (NUM_INPUTS == numread)	// if everything OK
        [self computeActivity];		// call computeActivity method
      else	// at end of stream, restart at beginning
      {
      	endFileChoice = NXRunAlertPanel(NULL,
				"End of input file or error reading...
				retry from beginning!",
	 			NULL, "No!", NULL);
	switch(endFileChoice)
	{
	  case NX_ALERTDEFAULT:		// OK ->read from beginning...
	    NXSeek(inputstream, 0, NX_FROMSTART);
	    [self nextInputSet:sender];
	    break;
	  case NX_ALERTALTERNATE:		// No! do nothing yet...
	    break;
	  default:
	    break;
	}
      }
    }
    else
    	NXRunAlertPanel(NULL,
	"Must Open Network and Input files first!", NULL, NULL, NULL);

    return self;
}


// calculate the output values for each interior and output neurons,
// then put the output neuron values in the outputstream.
// Finally, call the displayActivity method within NeuronView object, pointed
// to by theNeuronView outlet.
- computeActivity
{
  int i,j;
  double sum = 0.0;
  
  for (i = NUM_INPUTS; i < NUM_NODES; i++)
  {
  	for (j = 0; j < i; j++)
	  sum += (Network[i][j] * NeuronValues[j]);
	
	NeuronValues[i] = (1.0 / (1.0 + exp(-sum)));
	sum = 0.0;
  }
  
  if (TEMPCLEAR)  //does outputstream need to be connected to memory?
  {
    outputstream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
    TEMPCLEAR = FALSE;
  }

  // for each output, put value in the output stream...
  for(i = (NUM_NODES-NUM_OUTPUTS); i < NUM_NODES; i++)
      NXPrintf(outputstream,"%f ",NeuronValues[i]);
  NXPrintf(outputstream,"\n");
  
  [theNeuronView displayActivity];  // only fill the circles...

  return self;
}


// count the columns at right with all zeros = outputs
- getNumOutputs
{
  int i,j;
  
  NUM_OUTPUTS = NUM_NODES;
  
  // this could be modified to start looking from diagonal
  // instead of the rt. side. -oh, well, it works now!
  for (i = (NUM_NODES -1); i >=0; i--)
  {
    for (j = (NUM_NODES -1); j >=0; j--)
      if (Network[i][j] != 0)
        break;
    if (NUM_OUTPUTS >= (NUM_NODES -j))
      NUM_OUTPUTS = (NUM_NODES - j -1);
  }

  return self;
}


// count the rows with all zeros = #inputs
- getNumInputs
{
  int j,i=0;
  BOOL INPUT_OK = TRUE;
  
  NUM_INPUTS = 0;
  
  while  ((i < NUM_NODES) && (INPUT_OK))
  {
    for (j = 0; j < NUM_NODES; j++)
      if (Network[i][j] != 0)
      {
        INPUT_OK = FALSE;
	break;
      }
    i++;
  }
  NUM_INPUTS = i-1;
  
  return self;
}


@end
