% This function is used to find the error generated by neural network models for
% data that is already partitioned into training, validation, and test
% sets. inputData is the source data containing the feature vectors where
% the # of rows represent the # of samples and columns represent # of features
% (e.g. 'feat_vec_foo' - foo = 'train', 'val', or 'evl'), and labels (e.g. 
% 'labels_foo'). numNeurons sets the number of neurons in the model's single 
% hidden layer and outputData is the file that saves our results.

function mainFunc(inputData, numNeurons, outputData, numIterations)
    % Add basic functions to path
    addpath('../common');

    eval(['load ' inputData]); 
    [n_examples, n_features] = size(feat_vec_train);

    % Convert numeric label vector into a label matrix for training,
    % validation, and testing
    tr=[feat_vec_train labels_train];
    [nnout, class_names] = class2nnout(tr(:,n_features+1));
    tr = [tr(:,1:n_features) nnout];

    val=[feat_vec_devtest labels_devtest];
    [nnout_val, class_names] = class2nnout(val(:,n_features+1));
    val = [val(:,1:n_features) nnout_val];

    test=[feat_vec_evltest labels_evltest];
    [nnout_test, class_names] = class2nnout(test(:,n_features+1));
    test = [test(:,1:n_features) nnout_test];

    n_labels = size(nnout,2);

    %Normalize the data to mean=0 stdv=1 by using the means and variances of the training data
    [meanv, stdv, tr(:, 1 : n_features)] = normalize(tr(:, 1 : n_features), [], []);
    [~, ~, val(:, 1 : n_features)] = normalize(val(:, 1 : n_features), meanv, stdv);
    [~, ~, test(:, 1 : n_features)] = normalize(test(:, 1 : n_features), meanv, stdv);

    % Set neural network parameters
    info.no_class = n_labels;       % number of classes (number of labels)
    info.hidd = numNeurons;	       	% number of hidden neurons
    info.epochs = 1000;             % max number of training iterations (epochs)
    info.show =NaN;                 % show training results each 'show' epochs
    info.max_fail = 20;             % if error does not decrease on 'val' set in 
                                    % 'max_fail' consecutive epochs, stop the training
    info.showWindow = false;
    
    % Run over multiple iterations to find average error rates
    error = zeros(numIterations,1);
    error_val = zeros(numIterations,1);
    conf_matrix_val = cell(numIterations,1);
    conf_matrix = cell(numIterations,1);
    training_times=zeros(numIterations,1);
    for i = 1:numIterations
        tic;    % Start timing
        
        % Train the neural network
        [~, net,train_record]=neural_simple_class(tr,val,[],info);
        training_times(i) = toc;    % measure timing

        % Get neural network predictions on the test set
        nnout = sim(net,test(:,1:n_features)');
        nnout_val = sim(net,val(:,1:n_features)');

        % Transform the prediction and the true class values into a proper form
        prediction = nnout2class(nnout',class_names);
        prediction_val = nnout2class(nnout_val',class_names);

        % Calculate the accuracy/error rates
        [acc, conf_matrix{i}] = accuracy(prediction,labels_evltest,class_names); 
        [acc_val, conf_matrix_val{i}] = accuracy(prediction_val,labels_devtest,class_names);

        error(i) = 1-acc;
        error_val(i) = 1-acc_val;
    end

    % Calculate averages of error rates and training times
    avg_training_time = sum(training_times)/numIterations;
    avg_error = sum(error)/numIterations;     
    avg_error_val = sum(error_val)/numIterations;

    save(outputData, 'training_times', 'avg_training_time', 'avg_error', 'avg_error_val', 'error', 'error_val', 'conf_matrix', 'conf_matrix_val'); %, 'prediction', 'prediction_val','net', 'train_record')
    disp(['avg_error = ' num2str(avg_error)]);
    disp(['avg_error_val = ' num2str(avg_error_val)]);  

end
