#ifndef _JRN_NEURAL_NET_H_
#define _JRN_NEURAL_NET_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

/*
 * Programmer: Joshua New
 * Description: This program implements a feedforward, online, backprop
 *              neural network with classification over multiple categories
 * Output Files: This program outputs several files for analysis if the
 *               WRITE_FILES flag is activated.  The data is produced and
 *               written to the file every NUM_EPOCHS. The files are as follows:
 *                 errTrain.txt		- average MSE for training data; appended with
 *                                    percentage correct per letter on test data
 *                 errTrainHid.txt	- sum of the absolute value of hidden node errors
 *                 errTrainOut.txt	- sum of the absolute value of output node errors
 *                 errVal.txt		- average MSE for validation data
 *                 trainCorrect.txt	- number classified correctly on training data
 *                 valCorrect.txt	- number classified correctly on validation data
 */

/* GLOBAL CONSTANTS */
//Utility macros
#define WRITE_FILES 0		//Writes several output files (content listed above)
#define HALF_RESOLUTION 0	//Decreases the resolution to every other pixel

//Neural Network Topology
#define INPUT_NODES		1
#define HIDDEN_NODES	30
#define OUTPUT_NODES	2
#define LEARNING_RATE	0.4f
//save and validate every N steps
#define NUM_EPOCHS		5
//stop after this many epochs
#define MAX_EPOCHS		240

//Activation struct for Neural Net
typedef struct {
	float* in;		//input vector to nn
	float* sup;		//supervisor vector to nn
	float* hid;		//activation of hidden nodes
	float* actual;	//activation of output nodes, neural net output
	float* errOut;	//error of output nodes
	float* errHid;	//error of hidden nodes
	int* numCorrect;	//counts the number classified correctly for each letter
	float* sumErrOut;	//average error of output nodes
	float* sumErrHid;	//average error of hidden nodes
	float mse;		//mean-squared error (common method of detecting error)
	int nguess;		//number of classifications made in last train session
	int* guess;		//store the guesses/classifications of the neural net 
} Activation;

//Main neural network struct
typedef struct {
	int id;
	int nepochs;	//number of epochs this NN has been learning
	float lr;		//learning rate
	int nin;		//number of input nodes
	int nhid;		//assumes 1 hidden layer (sufficient for most problems)
	int nout;		//number of output nodes
	float** wih;	//input-to-hidden weights
	float** who;	//hidden-to-output weights
	Activation a;	//store activations during learning (not absolutely necessary)
} NeuralNet;

//Input data struct
typedef struct {
	int nitems;		//number of items to be classified
	int nin;		//number of pixels
	int nsup;		//number of letters
	int* item;		//ID of each of our items
	float** in;		//letter,file,data (float for future use)
	float** sup;	//letter,file,data (float for future use)
} Data;

#ifdef __cplusplus
extern "C" {
#endif

//---Neural Network
//--Main functions
//Set the parameters for our neural network and allocate space
void createNN(NeuralNet* nn, int id, int nin, int nhid, int nout,float lr);

//Allocate memory for our neural network
void initNN(NeuralNet* nn);

//Assign random weights to our neural network
void assignRandomWeights(NeuralNet* nn);

//Main learn/validate/test loop for 1 epoch on all data
int onlineNN(NeuralNet* nn, Data* learn, int train);

//Calculate activations throughout the neural network
void feedForward(NeuralNet* nn);

//Correct weights so our neural network gets better
void backPropagate(NeuralNet* nn);

//--Utility functions
//Sigmoid activation function
float sigmoid(float x);

//Derivative of activation function
float sigmoidDerivative(float x);

//Select the most highly activated letter
int hardmax(int nout, float *vector);

//Select the most highly activated letter probabilistically (derivative exists)
void softmax(int nout, float* vector);

//--IO functions
//Allows saving of agent's Q-table to a file
// Header: NN ID, number of epochs, number of learning iterations
//         number input nodes, number hidden, number out, learning rate
//         input-to-hidden weights, hidden-to-output weights
void saveAgent(NeuralNet* nn);

//Allows the loading of agent's Q-table from a file
void loadAgent(char* filename, NeuralNet* nn);

#ifdef __cplusplus
}
#endif

#endif
