public double valueAt(double[] param) {
      double[] sdInv = new double[nVariables];

      for (int i = 0; i < nVariables; i++) {
        R.setEntry(i, i, 1.0 - param[i]);
        sdInv[i] = 1.0 / Sinv.getEntry(i, i);
      }

      DiagonalMatrix diagSdInv = new DiagonalMatrix(sdInv);

      EigenDecomposition eigen = new EigenDecomposition(R);
      RealMatrix eigenVectors = eigen.getV().getSubMatrix(0, nVariables - 1, 0, nFactors - 1);

      double[] ev = new double[nFactors];
      for (int i = 0; i < nFactors; i++) {
        ev[i] = Math.sqrt(eigen.getRealEigenvalue(i));
      }
      DiagonalMatrix evMatrix =
          new DiagonalMatrix(
              ev); // USE Apache version of Diagonal matrix when upgrade to version 3.2
      RealMatrix LAMBDA = eigenVectors.multiply(evMatrix);
      RealMatrix SIGMA = (LAMBDA.multiply(LAMBDA.transpose()));

      double value = 0.0;
      RealMatrix DIF = R.subtract(SIGMA);
      for (int i = 0; i < DIF.getRowDimension(); i++) {
        for (int j = 0; j < DIF.getColumnDimension(); j++) {
          value = DIF.getEntry(i, j);
          DIF.setEntry(i, j, Math.pow(value, 2));
        }
      }

      RealMatrix RESID = diagSdInv.multiply(DIF).multiply(diagSdInv);

      double sum = 0.0;
      for (int i = 0; i < RESID.getRowDimension(); i++) {
        for (int j = 0; j < RESID.getColumnDimension(); j++) {
          sum += RESID.getEntry(i, j);
        }
      }
      return sum;
    }
  private void computeFactorLoadings(double[] x) {
    uniqueness = x;
    communality = new double[nVariables];

    for (int i = 0; i < nVariables; i++) {
      R.setEntry(i, i, 1.0 - x[i]);
    }

    EigenDecomposition E = new EigenDecomposition(R);
    RealMatrix L = E.getV().getSubMatrix(0, nVariables - 1, 0, nFactors - 1);
    double[] ev = new double[nFactors];
    for (int i = 0; i < nFactors; i++) {
      ev[i] = Math.sqrt(E.getRealEigenvalue(i));
    }
    DiagonalMatrix M = new DiagonalMatrix(ev);
    RealMatrix LOAD = L.multiply(M);

    // rotate factor loadings
    if (rotationMethod != RotationMethod.NONE) {
      GPArotation gpa = new GPArotation();
      RotationResults results = gpa.rotate(LOAD, rotationMethod);
      LOAD = results.getFactorLoadings();
    }

    Sum[] colSums = new Sum[nFactors];
    Sum[] colSumsSquares = new Sum[nFactors];

    for (int j = 0; j < nFactors; j++) {
      colSums[j] = new Sum();
      colSumsSquares[j] = new Sum();
    }

    factorLoading = new double[nVariables][nFactors];

    for (int i = 0; i < nVariables; i++) {
      for (int j = 0; j < nFactors; j++) {
        factorLoading[i][j] = LOAD.getEntry(i, j);
        colSums[j].increment(factorLoading[i][j]);
        colSumsSquares[j].increment(Math.pow(factorLoading[i][j], 2));
        communality[i] += Math.pow(factorLoading[i][j], 2);
      }
    }

    // check sign of factor
    double sign = 1.0;
    for (int i = 0; i < nVariables; i++) {
      for (int j = 0; j < nFactors; j++) {
        if (colSums[j].getResult() < 0) {
          sign = -1.0;
        } else {
          sign = 1.0;
        }
        factorLoading[i][j] = factorLoading[i][j] * sign;
      }
    }

    double totSumOfSquares = 0.0;
    sumsOfSquares = new double[nFactors];
    proportionOfExplainedVariance = new double[nFactors];
    proportionOfVariance = new double[nFactors];
    for (int j = 0; j < nFactors; j++) {
      sumsOfSquares[j] = colSumsSquares[j].getResult();
      totSumOfSquares += sumsOfSquares[j];
    }
    for (int j = 0; j < nFactors; j++) {
      proportionOfExplainedVariance[j] = sumsOfSquares[j] / totSumOfSquares;
      proportionOfVariance[j] = sumsOfSquares[j] / nVariables;
    }
  }