private void modifyWeights(
      DoubleMatrix trainingExample,
      DoubleMatrix result,
      DoubleMatrix output,
      double learningFactor,
      double momentum,
      List<DoubleMatrix> previousModifications,
      EvaluationContext evalCtx) {

    List<DoubleMatrix> errors = countErrors(result, output);
    List<Layer> layers = getLayers();

    evalCtx.resetContext();
    Iterator<ActivationFunction> activationFunctionIter = evalCtx.getActivationFunction();

    DoubleMatrix temporalResult = trainingExample;

    for (int i = 0; i < errors.size(); i++) {
      DoubleMatrix error = errors.get(i);
      int layerIndex = i + 1;
      Layer layer = layers.get(layerIndex);
      DoubleMatrix previousModification = previousModifications.get(i);

      ActivationFunction activationFunc = activationFunctionIter.next();
      ActivationFunctionDerivative derivative =
          DerivativeFactory.getInstance().getDerivative(activationFunc);

      if (layer.includeBias()) {
        temporalResult = addBiasInput(temporalResult);
      }

      DoubleMatrix oldVal = temporalResult.dup();
      temporalResult = layer.getWeights().mmul(temporalResult);
      temporalResult = activationFunc.eval(temporalResult);

      // dla kazdego neuronu w warstwie
      for (int j = 0; j < layer.getWeights().rows; j++) {

        double derVal = derivative.evaluate(temporalResult.get(j));
        DoubleMatrix oldDelta = previousModification.getRow(j);
        DoubleMatrix delta = oldVal.mul(derVal).mul(learningFactor).mul(error.get(j));
        delta = delta.transpose();
        delta = delta.add(oldDelta.mul(momentum));
        previousModification.putRow(j, delta);
        DoubleMatrix oldWeights = layer.getWeights().getRow(j);
        DoubleMatrix newWeights = oldWeights.add(delta);
        layer.getWeights().putRow(j, newWeights);
      }
    }
  }
  private void learn(
      DoubleMatrix trainingExample,
      DoubleMatrix result,
      double learningFactor,
      double momentum,
      List<DoubleMatrix> previousModifications,
      EvaluationContext evalCtx) {
    List<Layer> layers = getLayers();
    Layer outputLayer = layers.get(layers.size() - 1);

    if (outputLayer.getLayerSize() != result.getRows()) {
      throw new IllegalArgumentException("wrong vector size!");
    }

    evalCtx.resetContext();
    DoubleMatrix output = evaluate(trainingExample, evalCtx);
    modifyWeights(
        trainingExample, result, output, learningFactor, momentum, previousModifications, evalCtx);
  }
  public static void main(String... args) {
    // TEST
    //        BackpropagationNetwork network = new BackpropagationNetwork(new int[]{9, 3}, true);
    //
    //        List<DoubleMatrix> trainingSet = Arrays.asList(
    //                    new DoubleMatrix(new double[]{0, 0, 0, 0, 0, 0, 0, 0, 0}),
    //                    new DoubleMatrix(new double[]{1, 1, 1, 0, 1, 0, 0, 1, 0}),
    //                    new DoubleMatrix(new double[]{0, 1, 0, 1, 1, 1, 0, 1, 0})
    //                );
    //
    //        List<DoubleMatrix> resultSet = Arrays.asList(
    //                    new DoubleMatrix(new double[]{1, 0, 0}),
    //                    new DoubleMatrix(new double[]{0, 1, 0}),
    //                    new DoubleMatrix(new double[]{0, 0, 1})
    //                );
    //
    //        EvaluationContext ctx = new EvaluationContext(Arrays.asList( (ActivationFunction)new
    // Sigmoid()));
    //        network.learn(trainingSet, resultSet, 0.3, 30000, ctx);
    //
    //        for(DoubleMatrix t : trainingSet){
    //            ctx.resetContext();
    //            network.evaluate(t, ctx).print();
    //        }
    //
    //        ctx.resetContext();

    //        BackpropagationNetwork network = new BackpropagationNetwork(new int[]{2, 6, 1}, true);
    //
    //        List<DoubleMatrix> trainingSet = Arrays.asList(
    //                    new DoubleMatrix(new double[]{0, 0}),
    //                    new DoubleMatrix(new double[]{1, 0}),
    //                    new DoubleMatrix(new double[]{1, 1}),
    //                    new DoubleMatrix(new double[]{0, 1})
    //                );
    //
    //        List<DoubleMatrix> resultSet = Arrays.asList(
    //                    new DoubleMatrix(new double[]{0}),
    //                    new DoubleMatrix(new double[]{1}),
    //                    new DoubleMatrix(new double[]{0}),
    //                    new DoubleMatrix(new double[]{1})
    //                );
    //
    //        EvaluationContext ctx = new EvaluationContext(Arrays.asList( (ActivationFunction)new
    // Sigmoid(), new Sigmoid()));
    //        network.learn(trainingSet, resultSet, 0.4, 0.5, 20000, ctx);
    //
    //        for(DoubleMatrix t : trainingSet){
    //            ctx.resetContext();
    //            network.evaluate(t, ctx).print();
    //        }
    //
    //        ctx.resetContext();

    BackpropagationNetwork network = new BackpropagationNetwork(new int[] {3, 8, 1}, true);

    List<DoubleMatrix> trainingSet =
        Arrays.asList(
            new DoubleMatrix(new double[] {0, 0, 0}),
            new DoubleMatrix(new double[] {0, 0, 1}),
            new DoubleMatrix(new double[] {0, 1, 0}),
            new DoubleMatrix(new double[] {0, 1, 1}),
            new DoubleMatrix(new double[] {1, 0, 0}),
            new DoubleMatrix(new double[] {1, 0, 1}),
            new DoubleMatrix(new double[] {1, 1, 0}),
            new DoubleMatrix(new double[] {1, 1, 1}));

    List<DoubleMatrix> resultSet =
        Arrays.asList(
            new DoubleMatrix(new double[] {0}),
            new DoubleMatrix(new double[] {1}),
            new DoubleMatrix(new double[] {1}),
            new DoubleMatrix(new double[] {0}),
            new DoubleMatrix(new double[] {1}),
            new DoubleMatrix(new double[] {0}),
            new DoubleMatrix(new double[] {0}),
            new DoubleMatrix(new double[] {1}));

    EvaluationContext ctx =
        new EvaluationContext(Arrays.asList((ActivationFunction) new Sigmoid(), new Sigmoid()));
    network.learn(trainingSet, resultSet, 0.2, 0.1, 50000, ctx);

    for (DoubleMatrix t : trainingSet) {
      ctx.resetContext();
      network.evaluate(t, ctx).print();
    }
  }