/**
   * Compute the raw matrix for an instance run set
   *
   * @param data the data the instance runs
   * @return the raw matrix
   */
  private final IMatrix __computeInstanceRuns(final IInstanceRuns data) {
    final _List list;
    final DimensionTransformation xIn, yIn;
    final Transformation yOut;
    final IDimension timeDim, goalDim;

    xIn = this.getXAxisTransformation();
    try (final TransformationFunction xInFunc = xIn.use(data)) {
      yIn = this.getYAxisInputTransformation();
      try (final TransformationFunction yInFunc = yIn.use(data)) {
        yOut = this.getYAxisOutputTransformation();
        try (final TransformationFunction yOutFunc = yOut.use(data)) {

          timeDim = xIn.getDimension();
          goalDim = yIn.getDimension();

          if (goalDim.getDataType().isInteger() && yInFunc.isLongArithmeticAccurate()) {
            if (timeDim.getDataType().isInteger()) {
              list =
                  new _LongTimeLongGoal(
                      timeDim, goalDim, this.m_criterion, yInFunc, this.m_goalValueLong);
            } else {
              list =
                  new _DoubleTimeLongGoal(
                      timeDim, goalDim, this.m_criterion, yInFunc, this.m_goalValueLong);
            }
          } else {
            if (timeDim.getDataType().isInteger()) {
              list =
                  new _LongTimeDoubleGoal(
                      timeDim, goalDim, this.m_criterion, yInFunc, this.m_goalValueDouble);
            } else {
              list =
                  new _DoubleTimeDoubleGoal(
                      timeDim, goalDim, this.m_criterion, yInFunc, this.m_goalValueDouble);
            }
          }

          for (final IRun run : data.getData()) {
            list._addRun(run);
          }

          return list._toMatrix(xInFunc, yOutFunc);
        }
      }
    }
  }
  /**
   * Create the ECDF attribute
   *
   * @param xAxisTransformation the transformation to be applied to the {@code x}-axis
   * @param yAxisInputTransformation the transformation to be applied to the data of the {@code y}
   *     -axis before being fed to the actual computation
   * @param yAxisOutputTransformation the transformation of the result of the function applied to
   *     the data on the {@code y}-axis.
   * @param goalValue the goal value
   * @param criterion the goal comparison criterion
   * @param aggregate the method to aggregate the ECDFs
   */
  public ECDF(
      final DimensionTransformation xAxisTransformation,
      final DimensionTransformation yAxisInputTransformation,
      final Transformation yAxisOutputTransformation,
      final Number goalValue,
      final EComparison criterion,
      final StatisticalParameter aggregate) {
    super(
        EAttributeType.NEVER_STORED,
        xAxisTransformation,
        yAxisInputTransformation,
        yAxisOutputTransformation);

    final IDimension goalDim;

    if (goalValue == null) {
      throw new IllegalArgumentException( //
          "Goal value of ECDF cannot be null."); //$NON-NLS-1$
    }
    if (criterion == null) {
      throw new IllegalArgumentException( //
          "Comparison criterion of ECDF cannot be null."); //$NON-NLS-1$
    }
    if (aggregate == null) {
      throw new IllegalArgumentException( //
          "Aggregate to join ECDFs of different instance sets cannot be null."); //$NON-NLS-1$
    }

    goalDim = yAxisInputTransformation.getDimension();

    switch (goalDim.getDataType()) {
      case BYTE:
      case SHORT:
      case INT:
      case LONG:
        {
          if (yAxisInputTransformation.isLongArithmeticAccurate()) {
            this.m_useLongGoal = true;
            if ((NumericalTypes.getTypes(goalValue) & NumericalTypes.IS_LONG) != 0) {
              this.m_goalValueLong = goalValue.longValue();
            } else {
              this.m_goalValueLong = ECDF.__doubleToLong(goalValue.doubleValue(), criterion);
            }

            this.m_goalValueDouble = this.m_goalValueLong;
            break;
          }
          // fall through
        }
        // $FALL-THROUGH$
      default:
        {
          this.m_goalValueDouble = goalValue.doubleValue();
          this.m_useLongGoal = false;
          this.m_goalValueLong = ECDF.__doubleToLong(this.m_goalValueDouble, criterion);
        }
    }

    this.m_criterion = criterion;
    this.m_aggregate = aggregate;
  }
  /**
   * Create an instance of {@link ECDF} based on an experiment set and a configuration
   *
   * @param data the data (experiment set)
   * @param config the configuration
   * @return the instance of the aggregate
   */
  public static final ECDF create(final IExperimentSet data, final Configuration config) {
    DimensionTransformationParser dimParser;
    final DimensionTransformation xIn, yIn;
    final Transformation yOut;
    final StatisticalParameter aggregate;
    final IDimension goalDim;
    EComparison compare;
    Number goal;

    dimParser = new DimensionTransformationParser(data);
    xIn =
        config.get( //
            FunctionAttribute.X_AXIS_PARAM, dimParser, null);
    if (xIn == null) { //
      throw new IllegalArgumentException(
          "Must specify an x-dimension via parameter '" //$NON-NLS-1$
              + FunctionAttribute.X_AXIS_PARAM
              + '\'');
    }

    yIn = config.get(FunctionAttribute.Y_INPUT_AXIS_PARAM, dimParser, null);
    if (yIn == null) { //
      throw new IllegalArgumentException(
          "Must specify an input dimension for the y-axis via parameter '" //$NON-NLS-1$
              + FunctionAttribute.Y_INPUT_AXIS_PARAM
              + '\'');
    }

    dimParser = null;

    yOut =
        config.get(
            FunctionAttribute.Y_AXIS_OUTPUT_PARAM,
            new NamedParameterTransformationParser(data),
            new Transformation());

    aggregate =
        config.get(
            ECDF.AGGREGATE_PARAM,
            StatisticalParameterParser.getInstance(),
            ArithmeticMean.INSTANCE);

    goalDim = yIn.getDimension();

    if (goalDim.getDirection().isIncreasing()) {
      compare = EComparison.GREATER_OR_EQUAL;
      if (goalDim.getDataType().isInteger()) {
        goal = Long.valueOf(goalDim.getParser().getUpperBoundLong());
      } else {
        goal = Double.valueOf(goalDim.getParser().getUpperBoundDouble());
      }
    } else {
      compare = EComparison.LESS_OR_EQUAL;
      if (goalDim.getDataType().isInteger()) {
        goal = Long.valueOf(goalDim.getParser().getLowerBoundLong());
      } else {
        goal = Double.valueOf(goalDim.getParser().getLowerBoundDouble());
      }
    }

    goal = config.get(ECDF.GOAL_PARAM, AnyNumberParser.INSTANCE, goal);
    compare = config.get(ECDF.CRITERION_PARAM, ComparisonParser.getInstance(), compare);

    return new ECDF(xIn, yIn, yOut, goal, compare, aggregate);
  }
  /** {@inheritDoc} */
  @Override
  public ETextCase printDescription(final ITextOutput textOut, final ETextCase textCase) {
    final DimensionTransformation xIn, yIn;
    final Transformation yOut;
    ETextCase use;

    use = super.printDescription(textOut, textCase).nextCase();

    yIn = this.getYAxisInputTransformation();
    xIn = this.getXAxisTransformation();
    yOut = this.getYAxisOutputTransformation();

    textOut.append(" The "); // $NON-NLS-1$
    if (textOut instanceof IComplexText) {
      try (final IMath math = ((IComplexText) textOut).inlineMath()) {
        this.yAxisMathRender(math, DefaultParameterRenderer.INSTANCE);
      }
    } else {
      this.yAxisMathRender(textOut, DefaultParameterRenderer.INSTANCE);
    }
    textOut.append( //
        " represents the fraction of runs which reach a value of "); //$NON-NLS-1$
    yIn.printShortName(textOut, use);
    textOut.append(' ');
    textOut.append(this.m_criterion.toString());
    textOut.append(' ');
    if (this.m_useLongGoal) {
      textOut.append(this.m_goalValueLong);
    } else {
      textOut.append(this.m_goalValueDouble);
    }
    textOut.append(" for a given ellapsed runtime measured in "); // $NON-NLS-1$
    xIn.getDimension().printShortName(textOut, use);

    if (yOut.isIdentityTransformation()) {
      textOut.append(". The "); // $NON-NLS-1$
      this.printShortName(textOut, use);
    } else {
      textOut.append( //
          ". We do not use these fractions directly, but instead compute "); //$NON-NLS-1$
      if (textOut instanceof IComplexText) {
        try (final IMath math = ((IComplexText) textOut).inlineMath()) {
          this.yAxisMathRender(math, DefaultParameterRenderer.INSTANCE);
        }
      } else {
        this.yAxisMathRender(textOut, DefaultParameterRenderer.INSTANCE);
      }
      textOut.append(". The result of this formula"); // $NON-NLS-1$
    }

    textOut.append( //
        " is always computed over the runs of an experiment for a given benchmark instance. If runs for multiple instances are available, we aggregate the results by computing their "); //$NON-NLS-1$
    this.m_aggregate.printLongName(textOut, use);
    textOut.append('.');

    if (!(xIn.isIdentityTransformation())) {
      textOut.append(" The x-axis does not represent the values of "); // $NON-NLS-1$
      xIn.getDimension().printShortName(textOut, use);
      textOut.append(" directly, but instead "); // $NON-NLS-1$
      if (textOut instanceof IComplexText) {
        try (final IMath math = ((IComplexText) textOut).inlineMath()) {
          xIn.mathRender(math, DefaultParameterRenderer.INSTANCE);
        }
      } else {
        xIn.mathRender(textOut, DefaultParameterRenderer.INSTANCE);
      }
      textOut.append('.');
    }

    if (yOut.isIdentityTransformation()) {
      textOut.append(" The "); // $NON-NLS-1$
      this.printShortName(textOut, use);
      textOut.append(" is always between "); // $NON-NLS-1$
      textOut.append(0);
      textOut.append(" and "); // $NON-NLS-1$
      textOut.append(1);
      textOut.append(" \u2012 and the higher it is, the better."); // $NON-NLS-1$
    }
    return use;
  }