private void eidgendecomposition(CMAEvolutionaryAlgorithm algorithm, int N, int flgforce) {

    if (countCUpdatesSinceEigenupdate == 0 && flgforce < 2) {
      return;
    }

    if (!algorithm.getFlgDiag()
        && flgforce <= 0
        && countCUpdatesSinceEigenupdate < 1.0 / algorithm.getCcov() / N / 5.0) {
      return;
    }

    if (algorithm.getFlgDiag()) {
      for (int i = 0; i < N; i++) {
        algorithm.getDiag()[i] = Math.sqrt(algorithm.getC()[i][i]);
      }
      algorithm.setCountCupdatesSinceEigenupdate(0);

    } else {
      // set B <- C
      for (int i = 0; i < N; i++) {
        for (int j = 0; j <= i; j++) {
          algorithm.getB()[i][j] = algorithm.getB()[j][i] = algorithm.getC()[i][j];
        }
      }

      // eigendecomposition:
      double[] offdiag = new double[N];
      tred2(N, algorithm.getB(), algorithm.getDiag(), offdiag);
      tql2(N, algorithm.getDiag(), offdiag, algorithm.getB());
      checkEigenSystem(N, algorithm.getC(), algorithm.getDiag(), algorithm.getB());

      for (int i = 0; i < N; i++) {
        if (algorithm.getDiag()[i] < 0.0) {
          if (algorithm.isDebug()) {
            System.err.println("ERROR - An eigenvalue has become negative.");
          }
        }
        algorithm.getDiag()[i] = Math.sqrt(algorithm.getDiag()[i]);
      }
      algorithm.setCountCupdatesSinceEigenupdate(0);
    }

    /*
     * TODO: Check if necessary
     */
    if (StatUtils.min(algorithm.getDiag()) == 0.0) {
      algorithm.setAxisRatio(Double.POSITIVE_INFINITY);
    } else {
      algorithm.setAxisRatio(
          StatUtils.max(algorithm.getDiag()) / StatUtils.min(algorithm.getDiag()));
    }
  } // eigendecomposition
  private void testAndCorrectNumerics(
      CMAEvolutionaryAlgorithm algorithm, int N, List<Individual> individuals) {

    /*
     * Flat fitness, Test if funtion values are identical
     */
    /*
     * we should sort the individuals by fitness value
     */
    Collections.sort(individuals, algorithm.getComparator());

    if (algorithm.getGenerations() > 0) {
      if (individuals.get(0).getFitness()
          == individuals
              .get((int) Math.min(algorithm.getLambda() - 1, algorithm.getLambda() / 2.0 + 1) - 1)
              .getFitness()) {
        if (debug) {
          System.err.println(
              "WARNING - Flat fitness landscape, consider reformulation of fitness,"
                  + "step-size increased");
        }

        algorithm.setSigma(
            algorithm.getSigma() * Math.exp((0.2 + algorithm.getCs() / algorithm.getDamps())));
      }
    }

    /*
     * Align (renormalize) scale C and (consequently sigma)
     */
    /*
     * e.g. for infinite stationary state simulations (noise handling needs to be introduces for
     * that)
     */
    /*
     * handling needs to be introduced for that)
     */
    double fac = 1;
    if (StatUtils.max(algorithm.getDiag()) < 1.0e-6) {
      fac = 1.0 / StatUtils.max(algorithm.getDiag());
    } else if (StatUtils.min(algorithm.getDiag()) > 1.0e+4) {
      fac = 1.0 / StatUtils.min(algorithm.getDiag());
    }

    if (fac != 1.0) {
      algorithm.setSigma(algorithm.getSigma() / fac);
      for (int i = 0; i < N; i++) {
        algorithm.getPc()[i] *= fac;
        algorithm.getDiag()[i] *= fac;
        for (int j = 0; j <= i; j++) {
          algorithm.getC()[i][j] *= fac * fac;
        }
      }
    }
  }
  @Override
  public List<Individual> operate(EvolutionaryAlgorithm algorithm, List<Individual> individuals)
      throws OperatorException {

    List<Individual> new_population = new ArrayList<Individual>(individuals.size());
    CMAEvolutionaryAlgorithm alg = (CMAEvolutionaryAlgorithm) algorithm;
    debug = algorithm.isDebug();

    double[] chromosome;

    int N = individuals.get(0).getDimension();

    countCUpdatesSinceEigenupdate = alg.getCountCupdatesSinceEigenupdate();
    // latest possibility to generate B and diagD:
    eidgendecomposition(alg, N, 0);

    // ensure minimal and maximal standard deviation:
    double minsqrtdiagC = Math.sqrt(StatUtils.min(alg.diag(alg.getC())));
    double maxsqrtdiagC = Math.sqrt(StatUtils.max(alg.diag(alg.getC())));

    /*
     * TODO: test if it is necessary *
     */
    for (int i = 0; i < individuals.size(); i++) {
      new_population.add((Individual) individuals.get(i).clone());
    }

    if (lowerStandardDeviation != null && lowerStandardDeviation.length > 0) {

      for (int i = 0; i < N; i++) {

        double d = lowerStandardDeviation[Math.min(i, lowerStandardDeviation.length - 1)];

        if (d > alg.getSigma() * minsqrtdiagC) {
          alg.setSigma(d / minsqrtdiagC);
        }
      }
    }

    if (upperStandardDeviation != null && upperStandardDeviation.length > 0) {

      for (int i = 0; i < N; i++) {

        double d = upperStandardDeviation[Math.min(i, upperStandardDeviation.length - 1)];

        if (d < alg.getSigma() * maxsqrtdiagC) {
          alg.setSigma(d / maxsqrtdiagC);
        }
      }
    }

    testAndCorrectNumerics(alg, N, new_population);

    double[] artmp = new double[N];

    /*
     * Sample the distribution
     */
    for (int iNk = 0; iNk < alg.getLambda(); iNk++) {

      chromosome = new_population.get(iNk).getChromosomeAt(0);

      if (alg.getFlgDiag()) {

        for (int i = 0; i < N; i++) {

          chromosome[i] =
              this.checkBounds(
                  alg,
                  alg.getxMean()[i] + alg.getSigma() * alg.getDiag()[i] * EAFRandom.nextGaussian());
        }

      } else {

        for (int i = 0; i < N; i++) {

          artmp[i] = alg.getDiag()[i] * EAFRandom.nextGaussian();
        }

        /*
         * add mutation (sigma * B * (D*z))
         */
        for (int i = 0; i < N; i++) {

          double sum = 0.0;
          for (int j = 0; j < N; j++) {

            sum += alg.getB()[i][j] * artmp[j];
          }

          chromosome[i] = this.checkBounds(alg, alg.getxMean()[i] + alg.getSigma() * sum);
        }
      }

      new_population.get(iNk).setChromosomeAt(0, chromosome);
    }

    return new_population;
  }