/// <summary>
        /// Calculates error values for all neurons of the network
        /// </summary>
        ///
        /// <param name="desiredOutput">Desired output vector</param>
        ///
        /// <returns>Returns summary squared error of the last layer divided by 2</returns>
        ///
        private double CalculateError(double[] desiredOutput)
        {
            // current and the next layers
            ActivationLayer layer, layerNext;

            // current and the next errors arrays
            double[] errors, errorsNext;
            // error values
            double error = 0, e, sum;
            // neuron's output value
            double output;
            // layers count
            int layersCount = network.LayersCount;

            // assume, that all neurons of the network have the same activation function
            IActivationFunction function = network[0][0].ActivationFunction;

            // calculate error values for the last layer first
            layer  = network[layersCount - 1];
            errors = neuronErrors[layersCount - 1];

            for (int i = 0, n = layer.NeuronsCount; i < n; i++)
            {
                output = layer[i].Output;
                // error of the neuron
                e = desiredOutput[i] - output;
                // error multiplied with activation function's derivative
                errors[i] = e * function.Derivative2(output);
                // squre the error and sum it
                error += (e * e);
            }

            // calculate error values for other layers
            for (int j = layersCount - 2; j >= 0; j--)
            {
                layer      = network[j];
                layerNext  = network[j + 1];
                errors     = neuronErrors[j];
                errorsNext = neuronErrors[j + 1];

                // for all neurons of the layer
                for (int i = 0, n = layer.NeuronsCount; i < n; i++)
                {
                    sum = 0.0;
                    // for all neurons of the next layer
                    for (int k = 0, m = layerNext.NeuronsCount; k < m; k++)
                    {
                        sum += errorsNext[k] * layerNext[k][i];
                    }
                    errors[i] = sum * function.Derivative2(layer[i].Output);
                }
            }

            // return squared error of the last layer divided by 2
            return(error / 2.0);
        }
        /// <summary>
        /// Calculates error values for all neurons of the network.
        /// </summary>
        ///
        /// <param name="desiredOutput">Desired output vector.</param>
        ///
        /// <returns>Returns summary squared error of the last layer divided by 2.</returns>
        ///
        private double CalculateError(double[] desiredOutput)
        {
            double error       = 0;
            int    layersCount = network.Layers.Length;

            // assume, that all neurons of the network have the same activation function
            IActivationFunction function = (network.Layers[0].Neurons[0] as ActivationNeuron).ActivationFunction;

            // calculate error values for the last layer first
            ActivationLayer layer = network.Layers[layersCount - 1] as ActivationLayer;

            double[] layerDerivatives = neuronErrors[layersCount - 1];

            for (int i = 0; i < layer.Neurons.Length; i++)
            {
                double output = layer.Neurons[i].Output;

                double e = output - desiredOutput[i];
                layerDerivatives[i] = e * function.Derivative2(output);
                error += (e * e);
            }

            // calculate error values for other layers
            for (int j = layersCount - 2; j >= 0; j--)
            {
                layer            = network.Layers[j] as ActivationLayer;
                layerDerivatives = neuronErrors[j];

                ActivationLayer layerNext       = network.Layers[j + 1] as ActivationLayer;
                double[]        nextDerivatives = neuronErrors[j + 1];

                // for all neurons of the layer
                for (int i = 0, n = layer.Neurons.Length; i < n; i++)
                {
                    double sum = 0.0;

                    for (int k = 0; k < layerNext.Neurons.Length; k++)
                    {
                        sum += nextDerivatives[k] * layerNext.Neurons[k].Weights[i];
                    }

                    layerDerivatives[i] = sum * function.Derivative2(layer.Neurons[i].Output);
                }
            }

            // return squared error of the last layer divided by 2
            return(error / 2.0);
        }
        /// <summary>
        /// Runs learning iteration
        /// </summary>
        ///
        /// <param name="input">input vector</param>
        /// <param name="output">desired output vector</param>
        ///
        /// <returns>Returns squared error divided by 2</returns>
        ///
        /// <remarks>Runs one learning iteration and updates neuron's
        /// weights.</remarks>
        ///
        public double Run(double[] input, double[] output)
        {
            // compute output of network
            double[] networkOutput = network.Compute(input);

            // get the only layer of the network
            ActivationLayer layer = network[0];
            // get activation function of the layer
            IActivationFunction activationFunction = layer[0].ActivationFunction;

            // summary network absolute error
            double error = 0.0;

            // update weights of each neuron
            for (int j = 0, k = layer.NeuronsCount; j < k; j++)
            {
                // get neuron of the layer
                ActivationNeuron neuron = layer[j];
                // calculate neuron's error
                double e = output[j] - networkOutput[j];
                // get activation function's derivative
                double functionDerivative = activationFunction.Derivative2(networkOutput[j]);

                // update weights
                for (int i = 0, n = neuron.InputsCount; i < n; i++)
                {
                    neuron[i] += learningRate * e * functionDerivative * input[i];
                }

                // update threshold value
                neuron.Threshold += learningRate * e * functionDerivative;

                // sum error
                error += (e * e);
            }

            return(error / 2);
        }