"use strict";
/**
* @author       Eric Kuhn <digit.sensitivee@gmail.com>
* @copyright    2017 Eric Kuhn
* @license      Eric Kuhn
*/
Object.defineProperty(exports, "__esModule", { value: true });
var Layer_1 = require("./Layer");
/* Neural Network class, composed of Neuron Layers */
var Network = (function () {
    function Network() {
        /* init parameters */
        this.layers = [];
    }
    /**
     * Generate the Network layers
     * @param  {[type]} _input   [Number of Neurons in Input layer]
     * @param  {[type]} _hiddens [Number of Neurons per Hidden layer]
     * @param  {[type]} _output  [Number of Neurons in Output layer]
     * @return {[type]}          [void]
     */
    Network.prototype.perceptronGeneration = function (_input, _hiddens, _output) {
        var index = 0;
        var previousNeurons = 0;
        var l = new Layer_1.Layer(index);
        /* Number of inputs will be set to 0 since it is an input layer */
        l.populate(_input, previousNeurons);
        /* number of input is size of previous layer */
        previousNeurons = _input;
        this.layers.push(l);
        index++;
        for (var i in _hiddens) {
            /* Repeat same process as first layer for each hidden layer */
            var l_1 = new Layer_1.Layer(index);
            l_1.populate(_hiddens[i], previousNeurons);
            previousNeurons = _hiddens[i];
            this.layers.push(l_1);
            index++;
        }
        var layer = new Layer_1.Layer(index);
        /* Number of input is equal to the size of the last hidden layer */
        layer.populate(_output, previousNeurons);
        this.layers.push(layer);
    };
    /**
     * Create a copy of the Network (neurons and weights)
     * Returns number of neurons per layer and a flat array of all weights.
     * @return {Object} [Network data]
     */
    Network.prototype.getSave = function () {
        var datas = {
            neurons: [],
            weights: [] // Weights of each Neuron's inputs.
        };
        for (var i in this.layers) {
            datas.neurons.push(this.layers[i].getNeurons().length);
            for (var j in this.layers[i].getNeurons()) {
                for (var k in this.layers[i].getNeurons()[j].getWeights()) {
                    /* push all input weights of each Neuron of each Layer into a flat array */
                    datas.weights.push(this.layers[i].getNeurons()[j].getWeights()[k]);
                }
            }
        }
        return datas;
    };
    /**
     * Apply network data (neurons and weights)
     * @param {[type]} _save [Copy of network data (neurons and weights)]
     */
    Network.prototype.setSave = function (_save) {
        var previousNeurons = 0;
        var index = 0;
        var indexWeights = 0;
        this.layers = [];
        for (var i in _save.neurons) {
            // Create and populate layers
            var layer = new Layer_1.Layer(index);
            layer.populate(_save.neurons[i], previousNeurons);
            for (var j in layer.getNeurons()) {
                for (var k in layer.getNeurons()[j].getWeights()) {
                    /* Apply neurons weights to each Neuron */
                    layer.getNeurons()[j].getWeights()[k] = _save.weights[indexWeights];
                    /* increment index of flat array */
                    indexWeights++;
                }
            }
            previousNeurons = _save.neurons[i];
            index++;
            this.layers.push(layer);
        }
    };
    /**
     * Compute the output of an input
     * @param  {[type]} _inputs [Set of inputs]
     * @return {Object}         [Network output]
     */
    Network.prototype.compute = function (_inputs) {
        /* Set the value of each Neuron in the input layer */
        for (var i in _inputs) {
            if (this.layers[0] && this.layers[0].getNeurons()[i]) {
                this.layers[0].getNeurons()[i].value = _inputs[i];
            }
        }
        /* Previous layer is input layer */
        var prevLayer = this.layers[0];
        for (var i = 1; i < this.layers.length; i++) {
            for (var j in this.layers[i].getNeurons()) {
                /* For each Neuron in each layer */
                var sum = 0;
                for (var k in prevLayer.getNeurons()) {
                    /* Every Neuron in the previous layer is an input to each Neuron in the next layer */
                    sum += prevLayer.getNeurons()[k].getValue() * this.layers[i].getNeurons()[j].getWeights()[k];
                }
                /* compute the activation of the Neuron */
                this.layers[i].getNeurons()[j].setValue(this.activation(sum));
            }
            prevLayer = this.layers[i];
        }
        /* all outputs of the Network */
        var out = [];
        var lastLayer = this.layers[this.layers.length - 1];
        for (var i in lastLayer.getNeurons()) {
            out.push(lastLayer.getNeurons()[i].getValue());
        }
        return out;
    };
    /**
     * Logistic activation function
     * @param  {number} a  [Input Value]
     * @return {number}    [Return Value]
     */
    Network.prototype.activation = function (a) {
        var ap = (-a) / 1;
        return (1 / (1 + Math.exp(ap)));
    };
    return Network;
}());
exports.Network = Network;
