code: package edu.cmu.tetrad.algcomparison.algorithm.multi;
  
  import edu.cmu.tetrad.algcomparison.algorithm.MultiDataSetAlgorithm;
  import edu.cmu.tetrad.algcomparison.score.ScoreWrapper;
  import edu.cmu.tetrad.algcomparison.utils.HasKnowledge;
  import edu.cmu.tetrad.algcomparison.utils.UsesScoreWrapper;
  import edu.cmu.tetrad.annotation.AlgType;
  import edu.cmu.tetrad.data.*;
  import edu.cmu.tetrad.graph.EdgeListGraph;
  import edu.cmu.tetrad.graph.Graph;
  import edu.cmu.tetrad.search.Fask;
  import edu.cmu.tetrad.util.Parameters;
  import edu.pitt.dbmi.algo.resampling.GeneralResamplingTest;
  import edu.pitt.dbmi.algo.resampling.ResamplingEdgeEnsemble;
  import java.text.DecimalFormat;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  /**
   * Wraps the IMaGES algorithm for continuous variables.
   * </p>
   * Requires that the parameter 'randomSelectionSize' be set to indicate how many
   * datasets should be taken at a time (randomly). This cannot given multiple
   * values.
   *
   * @author jdramsey
   */
  @edu.cmu.tetrad.annotation.Algorithm(
          name = "FASK Concatenated",
          command = "fask-concatenated",
          algoType = AlgType.forbid_latent_common_causes
  )
  public class FaskConcatenated implements MultiDataSetAlgorithm, HasKnowledge, UsesScoreWrapper {
  
      static final long serialVersionUID = 23L;
      private ScoreWrapper score;
      private IKnowledge knowledge = new Knowledge2();
  
      public FaskConcatenated() {
  
      }
  
      public FaskConcatenated(ScoreWrapper score) {
          this.score = score;
      }
  
      @Override
      public Graph search(List<DataModel> dataSets, Parameters parameters) {
          if (parameters.getInt("numberResampling") < 1) {
              List<DataSet> centered = new ArrayList<>();
  
              for (DataModel dataSet : dataSets) {
                  centered.add(DataUtils.standardizeData((DataSet) dataSet));
              }
  
              DataSet dataSet = DataUtils.concatenate(centered);
  
              dataSet.setNumberFormat(new DecimalFormat("0.000000000000000000"));
  
              Fask search = new Fask(dataSet, score.getScore(dataSet, parameters));
              search.setDepth(parameters.getInt("depth"));
              search.setPenaltyDiscount(parameters.getDouble("penaltyDiscount"));
              search.setExtraEdgeThreshold(parameters.getDouble("extraEdgeThreshold"));
              search.setDelta(parameters.getDouble("faskDelta"));
              search.setAlpha(parameters.getDouble("twoCycleAlpha"));
              search.setKnowledge(knowledge);
              
              return search.search();
          } else {
              FaskConcatenated algorithm = new FaskConcatenated(score);
  
              List<DataSet> datasets = new ArrayList<>();
  
              for (DataModel dataModel : dataSets) {
                  datasets.add((DataSet) dataModel);
              }
              GeneralResamplingTest search = new GeneralResamplingTest(datasets, algorithm, parameters.getInt("numberResampling"));
              search.setKnowledge(knowledge);
              
              search.setPercentResampleSize(parameters.getDouble("percentResampleSize"));
              search.setResamplingWithReplacement(parameters.getBoolean("resamplingWithReplacement"));
              
              ResamplingEdgeEnsemble edgeEnsemble = ResamplingEdgeEnsemble.Highest;
              switch (parameters.getInt("resamplingEnsemble", 1)) {
                  case 0:
                      edgeEnsemble = ResamplingEdgeEnsemble.Preserved;
                      break;
                  case 1:
                      edgeEnsemble = ResamplingEdgeEnsemble.Highest;
                      break;
                  case 2:
                      edgeEnsemble = ResamplingEdgeEnsemble.Majority;
              }
              search.setEdgeEnsemble(edgeEnsemble);
              search.setAddOriginalDataset(parameters.getBoolean("addOriginalDataset"));
              
              search.setParameters(parameters);
              search.setVerbose(parameters.getBoolean("verbose"));
              return search.search();
          }
      }
  
      @Override
      public Graph search(DataModel dataSet, Parameters parameters) {
          if (parameters.getInt("numberResampling") < 1) {
              return search(Collections.singletonList((DataModel) DataUtils.getContinuousDataSet(dataSet)), parameters);
          } else {
              FaskConcatenated algorithm = new FaskConcatenated(score);
  
              List<DataSet> dataSets = Collections.singletonList(DataUtils.getContinuousDataSet(dataSet));
              GeneralResamplingTest search = new GeneralResamplingTest(dataSets, algorithm, parameters.getInt("numberResampling"));
              search.setKnowledge(knowledge);
  
              search.setPercentResampleSize(parameters.getDouble("percentResampleSize"));
              search.setResamplingWithReplacement(parameters.getBoolean("resamplingWithReplacement"));
              
              ResamplingEdgeEnsemble edgeEnsemble = ResamplingEdgeEnsemble.Highest;
              switch (parameters.getInt("resamplingEnsemble", 1)) {
                  case 0:
                      edgeEnsemble = ResamplingEdgeEnsemble.Preserved;
                      break;
                  case 1:
                      edgeEnsemble = ResamplingEdgeEnsemble.Highest;
                      break;
                  case 2:
                      edgeEnsemble = ResamplingEdgeEnsemble.Majority;
              }
              search.setEdgeEnsemble(edgeEnsemble);
              search.setAddOriginalDataset(parameters.getBoolean("addOriginalDataset"));
              
              search.setParameters(parameters);
              search.setVerbose(parameters.getBoolean("verbose"));
              return search.search();
          }
      }
  
      @Override
      public Graph getComparisonGraph(Graph graph) {
          return new EdgeListGraph(graph);
      }
  
      @Override
      public String getDescription() {
          return "FASK Concatenated";
      }
  
      @Override
      public DataType getDataType() {
          return DataType.Continuous;
      }
  
      @Override
      public List<String> getParameters() {
          List<String> parameters = score.getParameters();
          parameters.add("depth");
          parameters.add("twoCycleAlpha");
          parameters.add("extraEdgeThreshold");
          parameters.add("faskDelta");
  
          parameters.add("numRuns");
          parameters.add("randomSelectionSize");
  
          // Resampling
          parameters.add("numberResampling");
          parameters.add("percentResampleSize");
          parameters.add("resamplingWithReplacement");
          parameters.add("resamplingEnsemble");
          parameters.add("addOriginalDataset");
          parameters.add("verbose");
  
          return parameters;
      }
  
      @Override
      public IKnowledge getKnowledge() {
          return knowledge;
      }
  
      @Override
      public void setKnowledge(IKnowledge knowledge) {
          this.knowledge = knowledge;
      }
  
      @Override
      public void setScoreWrapper(ScoreWrapper score) {
          this.score = score;
      }
      
      @Override
      public ScoreWrapper getScoreWarpper() {
          return score;
      }
  }