/**
   * Returns an array with the bevavior of the classifier with test dataset
   *
   * @param classifier Classifier to obtain the error
   * @return double[] Behavior array
   */
  public double[] getTestClassificationBehaviorArray(IClassifier classifier) {

    // Dataset to be used
    DoubleTransposedDataSet dataset;
    if (dataNormalized) dataset = scaledTestData;
    else dataset = unscaledTestData;

    // Resulting array
    double[] result = new double[dataset.getNofobservations()];

    // Obtained outputs with this dataSet
    byte obtained[][] = classifier.classify(dataset.getAllInputs());

    // Expected outputs
    double expected[][] = dataset.getAllOutputs();

    // Init result
    for (int j = 0; j < dataset.getNofobservations(); j++) result[j] = 1;

    // Put a 0 in observations where the expected is not
    // the same than the observed
    for (int o = 0; o < obtained.length; o++) // For each output (o)
    for (int e = 0; e < obtained[o].length; e++) // For each example (e)
      if (obtained[o][e] != expected[o][e]) if (result[e] == 1) result[e] = 0;

    return result;
  }
  /**
   * Configuration parameters for NeuralNetEvaluator are:
   *
   * <ul>
   *   <li><code>train-data: complex</code> Train data set used in individuals evaluation.
   *       <ul>
   *         <li><code>train-data[@file-name] String </code> File name of train data
   *       </ul>
   *   <li><code>test-data: complex</code> Test data set used in individuals evaluation.
   *       <ul>
   *         <li><code>test-data[@file-name] String </code> File name of test data
   *       </ul>
   *   <li><code>[@normalize-data]: boolean (default = false)</code> If this parameter is set to
   *       <code>true</code> data sets values are normalizated after reading their contents
   *   <li><code>[input-interval] (complex)</code> Input interval of normalization.
   *   <li><code>[output-interval] (complex)</code> Output interval of normalization.
   * </ul>
   *
   * <p>
   *
   * @param settings Configuration object from which the properties are read
   */
  public void configure(Configuration settings) {

    // Set trainData
    unscaledTrainData = new DoubleTransposedDataSet();
    unscaledTrainData.configure(settings.subset("train-data"));

    // Set testData
    unscaledTestData = new DoubleTransposedDataSet();
    unscaledTestData.configure(settings.subset("test-data"));

    // Set normalizer
    normalizer = new Normalizer();

    // Set dataNormalized
    dataNormalized = settings.getBoolean("[@normalize-data]", false);

    // Set dataNormalized
    logTransformation = settings.getBoolean("[@log-input-data]", false);

    if (dataNormalized) {
      // Normalization Input Interval
      Interval interval = new Interval();
      // Configure interval
      interval.configure(settings.subset("input-interval"));
      // Set interval
      setInputInterval(interval);
      // Normalization Output Interval
      interval = new Interval();
      // Configure range
      interval.configure(settings.subset("output-interval"));
      // Set interval
      setOutputInterval(interval);
    }
  }
  /**
   * Read and normalize evaluator datasets
   *
   * @param schema Schema of the dataset
   * @param traindata IDataset with the training data
   * @param testdata IDataset with the test data
   */
  public void readData(byte[] schema, IDataset traindata, IDataset testdata) {

    // Read trainData
    try {
      unscaledTrainData.read(schema, traindata);
    } catch (DatasetException e1) {
      e1.printStackTrace();
    }

    // Read testData
    try {
      unscaledTestData.read(schema, testdata);
    } catch (DatasetException e1) {
      e1.printStackTrace();
    }

    normalizeData();
  }
  /**
   * Read and normalize evaluator datasets
   *
   * @throws IOException Data not correct
   * @throws NumberFormatException Format of data not correct
   */
  public void readData() throws IOException, NumberFormatException {

    // Read trainData
    try {
      unscaledTrainData.read();
    } catch (IOException e) {
      throw new IOException("trainData IOException: " + e.getLocalizedMessage());
    } catch (NumberFormatException e) {
      throw new NumberFormatException(
          "trainData NumberFormatException: " + e.getLocalizedMessage());
    }

    // Read testData
    try {
      unscaledTestData.read();
    } catch (IOException e) {
      throw new IOException("testData IOException: " + e.getLocalizedMessage());
    } catch (NumberFormatException e) {
      throw new NumberFormatException("testData NumberFormatException: " + e.getLocalizedMessage());
    }

    normalizeData();
  }
  /**
   * Returns the train error value of a neural net with an specified error function
   *
   * @param nnind Neural net to obtain the error
   * @param errorFunction Error function to obtain the error
   * @return double Train error value
   */
  public double getTrainClassificationError(
      IClassifier classifier, IErrorFunction<byte[][]> errorFunction) {

    // Dataset to be used
    DoubleTransposedDataSet dataset;
    if (dataNormalized) dataset = scaledTrainData;
    else dataset = unscaledTrainData;

    // Obtained outputs with this dataSet
    byte obtained[][] = classifier.classify(dataset.getAllInputs());

    // Casting the outputs
    double doubleExpected[][] = dataset.getAllOutputs();
    byte expected[][] = new byte[doubleExpected.length][doubleExpected[0].length];
    for (int i = 0; i < doubleExpected.length; i++)
      for (int j = 0; j < doubleExpected[i].length; j++)
        expected[i][j] = (byte) doubleExpected[i][j];

    // Obtain error
    double error = errorFunction.calculateError(obtained, expected);

    // Return train error value
    return error;
  }
  /** Normalize data */
  private void normalizeData() {
    if (dataNormalized) {
      // Obtain maximum and minimum values
      unscaledMin = new double[unscaledTrainData.getNofvariables()];
      unscaledMax = new double[unscaledTrainData.getNofvariables()];
      double temp;
      for (int i = 0; i < unscaledTrainData.getNofvariables(); i++) {
        unscaledMax[i] = unscaledTrainData.getMaxValueOf(i);
        temp = unscaledTestData.getMaxValueOf(i);
        if (temp > unscaledMax[i]) unscaledMax[i] = temp;
        unscaledMin[i] = unscaledTrainData.getMinValueOf(i);
        temp = unscaledTestData.getMinValueOf(i);
        if (temp < unscaledMin[i]) unscaledMin[i] = temp;
      }

      // Obtain maximum and minimum desired values
      double[] scaledMin = new double[unscaledTrainData.getNofvariables()];
      double[] scaledMax = new double[unscaledTrainData.getNofvariables()];
      for (int i = 0; i < unscaledTrainData.getNofvariables(); i++) {
        if (i < unscaledTrainData.getNofinputs()) {
          scaledMin[i] = inputInterval.getLeft();
          scaledMax[i] = inputInterval.getRight();
        } else {
          scaledMin[i] = outputInterval.getLeft();
          scaledMax[i] = outputInterval.getRight();
        }
      }

      // Normalize trainData
      scaledTrainData = unscaledTrainData.copy();
      normalizer.scaleDS(scaledTrainData, scaledMax, scaledMin, unscaledMax, unscaledMin);

      // Normalize trainData
      scaledTestData = unscaledTestData.copy();
      normalizer.scaleDS(scaledTestData, scaledMax, scaledMin, unscaledMax, unscaledMin);
    }

    // Remove constant inputs
    int newNofinputs = 0;
    boolean[] toRemove = unscaledTrainData.obtainConstantsInputs();

    for (int i = 0; i < toRemove.length; i++) if (!toRemove[i]) newNofinputs++;

    unscaledTrainData.removeInputs(toRemove, newNofinputs);
    unscaledTestData.removeInputs(toRemove, newNofinputs);

    if (dataNormalized) {
      scaledTrainData.removeInputs(toRemove, newNofinputs);
      scaledTestData.removeInputs(toRemove, newNofinputs);

      // Log transformation
      if (logTransformation) {
        double[][] inputs = scaledTrainData.getAllInputs();

        for (int i = 0; i < inputs.length; i++)
          for (int j = 0; j < inputs[i].length; j++) inputs[i][j] = Math.log(inputs[i][j]);

        inputs = scaledTestData.getAllInputs();

        for (int i = 0; i < inputs.length; i++)
          for (int j = 0; j < inputs[i].length; j++) inputs[i][j] = Math.log(inputs[i][j]);
      }

    }

    // Log transformation
    else if (logTransformation) {
      double[][] inputs = unscaledTrainData.getAllInputs();

      for (int i = 0; i < inputs.length; i++)
        for (int j = 0; j < inputs[i].length; j++) inputs[i][j] = Math.log(inputs[i][j]);

      inputs = unscaledTestData.getAllInputs();

      for (int i = 0; i < inputs.length; i++)
        for (int j = 0; j < inputs[i].length; j++) inputs[i][j] = Math.log(inputs[i][j]);
    }
  }