public List <double> FeedForward(double[] input)
        {
            if (input.Length != InputLayer.Count)
            {
                throw new ArgumentException("input length has to be the same as the InputLayer length");
            }

            // calculate input activations
            for (int i = 0; i < input.Length; i++)
            {
                InputLayer[i].ActivationValue = input[i];
            }

            // calculate hidden layer activations
            // i : LayerIndex in HiddenLayers
            // j : NeuronIndex in HiddenLayers[i]
            for (int i = 0; i < HiddenLayers.Count; i++)
            {
                var previousLayer = HiddenLayers.ElementAtOrDefault(i - 1) ?? InputLayer;
                for (int j = 0; j < HiddenLayers[i].Count; j++)
                {
                    HiddenLayers[i][j].ActivationValue = CalculateWeightedSum(previousLayer, j);
                }
            }

            // calculate output activations
            // i : NeuronIndex in OutputLayer
            var lastLayer = HiddenLayers.LastOrDefault() ?? InputLayer;

            for (int i = 0; i < OutputLayer.Count; i++)
            {
                var currentNeuron = OutputLayer[i];
                OutputLayer[i].ActivationValue = CalculateWeightedSum(lastLayer, i);
            }

            // return only values of output layer
            return(OutputLayer.Select(x => x.ActivationValue).ToList());
        }