public DataSet simulateDataCholesky(
      int sampleSize, TetradMatrix covar, List<Node> variableNodes) {
    List<Node> variables = new LinkedList<Node>();

    for (Node node : variableNodes) {
      variables.add(node);
    }

    List<Node> newVariables = new ArrayList<Node>();

    for (Node node : variables) {
      ContinuousVariable continuousVariable = new ContinuousVariable(node.getName());
      continuousVariable.setNodeType(node.getNodeType());
      newVariables.add(continuousVariable);
    }

    TetradMatrix impliedCovar = covar;

    DataSet fullDataSet = new ColtDataSet(sampleSize, newVariables);
    TetradMatrix cholesky = MatrixUtils.choleskyC(impliedCovar);

    // Simulate the data by repeatedly calling the Cholesky.exogenousData
    // method. Store only the data for the measured variables.
    ROW:
    for (int row = 0; row < sampleSize; row++) {

      // Step 1. Generate normal samples.
      double exoData[] = new double[cholesky.rows()];

      for (int i = 0; i < exoData.length; i++) {
        exoData[i] = RandomUtil.getInstance().nextNormal(0, 1);
        //            exoData[i] = randomUtil.nextUniform(-1, 1);
      }

      // Step 2. Multiply by cholesky to get correct covariance.
      double point[] = new double[exoData.length];

      for (int i = 0; i < exoData.length; i++) {
        double sum = 0.0;

        for (int j = 0; j <= i; j++) {
          sum += cholesky.get(i, j) * exoData[j];
        }

        point[i] = sum;
      }

      double rowData[] = point;

      for (int col = 0; col < variables.size(); col++) {
        int index = variableNodes.indexOf(variables.get(col));
        double value = rowData[index];

        if (Double.isNaN(value) || Double.isInfinite(value)) {
          throw new IllegalArgumentException("Value out of range: " + value);
        }

        fullDataSet.setDouble(row, col, value);
      }
    }

    return DataUtils.restrictToMeasured(fullDataSet);
  }