gt:         values = fullSet.values(best.getAttr());
context: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.mahout.classifier.df.builder;
  
  import org.apache.mahout.classifier.df.data.Data;
  import org.apache.mahout.classifier.df.data.Dataset;
  import org.apache.mahout.classifier.df.data.Instance;
  import org.apache.mahout.classifier.df.data.conditions.Condition;
  import org.apache.mahout.classifier.df.node.CategoricalNode;
  import org.apache.mahout.classifier.df.node.Leaf;
  import org.apache.mahout.classifier.df.node.Node;
  import org.apache.mahout.classifier.df.node.NumericalNode;
  import org.apache.mahout.classifier.df.split.IgSplit;
  import org.apache.mahout.classifier.df.split.OptIgSplit;
  import org.apache.mahout.classifier.df.split.RegressionSplit;
  import org.apache.mahout.classifier.df.split.Split;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.Random;
  
  /**
   * Builds a classification tree or regression tree<br>
   * A classification tree is built when the criterion variable is the categorical attribute.<br>
   * A regression tree is built when the criterion variable is the numerical attribute.
   */
  public class DecisionTreeBuilder implements TreeBuilder {
  
    private static final Logger log = LoggerFactory.getLogger(DecisionTreeBuilder.class);
  
    private static final int[] NO_ATTRIBUTES = new int[0];
    private static final double EPSILON = 1.0e-6;
  
    /**
     * indicates which CATEGORICAL attributes have already been selected in the parent nodes
     */
    private boolean[] selected;
    /**
     * number of attributes to select randomly at each node
     */
    private int m;
    /**
     * IgSplit implementation
     */
    private IgSplit igSplit;
    /**
     * tree is complemented
     */
    private boolean complemented = true;
    /**
     * minimum number for split
     */
    private double minSplitNum = 2.0;
    /**
     * minimum proportion of the total variance for split
     */
    private double minVarianceProportion = 1.0e-3;
    /**
     * full set data
     */
    private Data fullSet;
    /**
     * minimum variance for split
     */
    private double minVariance = Double.NaN;
  
    public void setM(int m) {
      this.m = m;
    }
  
    public void setIgSplit(IgSplit igSplit) {
      this.igSplit = igSplit;
    }
  
    public void setComplemented(boolean complemented) {
      this.complemented = complemented;
    }
  
    public void setMinSplitNum(int minSplitNum) {
      this.minSplitNum = minSplitNum;
    }
  
    public void setMinVarianceProportion(double minVarianceProportion) {
      this.minVarianceProportion = minVarianceProportion;
    }
  
    @Override
    public Node build(Random rng, Data data) {
      if (selected == null) {
        selected = new boolean[data.getDataset().nbAttributes()];
        selected[data.getDataset().getLabelId()] = true; // never select the label
      }
      if (m == 0) {
        // set default m
        double e = data.getDataset().nbAttributes() - 1;
        if (data.getDataset().isNumerical(data.getDataset().getLabelId())) {
          // regression
          m = (int) Math.ceil(e / 3.0);
        } else {
          // classification
          m = (int) Math.ceil(Math.sqrt(e));
        }
      }
  
      if (data.isEmpty()) {
        return new Leaf(Double.NaN);
      }
  
      double sum = 0.0;
      if (data.getDataset().isNumerical(data.getDataset().getLabelId())) {
        // regression
        // sum and sum squared of a label is computed
        double sumSquared = 0.0;
        for (int i = 0; i < data.size(); i++) {
          double label = data.getDataset().getLabel(data.get(i));
          sum += label;
          sumSquared += label * label;
        }
  
        // computes the variance
        double var = sumSquared - (sum * sum) / data.size();
  
        // computes the minimum variance
        if (Double.compare(minVariance, Double.NaN) == 0) {
          minVariance = var / data.size() * minVarianceProportion;
          log.debug("minVariance:{}", minVariance);
        }
  
        // variance is compared with minimum variance
        if ((var / data.size()) < minVariance) {
          log.debug("variance({}) < minVariance({}) Leaf({})", var / data.size(), minVariance, sum / data.size());
          return new Leaf(sum / data.size());
        }
      } else {
        // classification
        if (isIdentical(data)) {
          return new Leaf(data.majorityLabel(rng));
        }
        if (data.identicalLabel()) {
          return new Leaf(data.getDataset().getLabel(data.get(0)));
        }
      }
  
      // store full set data
      if (fullSet == null) {
        fullSet = data;
      }
  
      int[] attributes = randomAttributes(rng, selected, m);
      if (attributes == null || attributes.length == 0) {
        // we tried all the attributes and could not split the data anymore
        double label;
        if (data.getDataset().isNumerical(data.getDataset().getLabelId())) {
          // regression
          label = sum / data.size();
        } else {
          // classification
          label = data.majorityLabel(rng);
        }
        log.warn("attribute which can be selected is not found Leaf({})", label);
        return new Leaf(label);
      }
  
      if (igSplit == null) {
        if (data.getDataset().isNumerical(data.getDataset().getLabelId())) {
          // regression
          igSplit = new RegressionSplit();
        } else {
          // classification
          igSplit = new OptIgSplit();
        }
      }
  
      // find the best split
      Split best = null;
      for (int attr : attributes) {
        Split split = igSplit.computeSplit(data, attr);
        if (best == null || best.getIg() < split.getIg()) {
          best = split;
        }
      }
  
      // information gain is near to zero.
      if (best.getIg() < EPSILON) {
        double label;
        if (data.getDataset().isNumerical(data.getDataset().getLabelId())) {
          label = sum / data.size();
        } else {
          label = data.majorityLabel(rng);
        }
        log.debug("ig is near to zero Leaf({})", label);
        return new Leaf(label);
      }
  
      log.debug("best split attr:{}, split:{}, ig:{}", best.getAttr(), best.getSplit(), best.getIg());
  
      boolean alreadySelected = selected[best.getAttr()];
      if (alreadySelected) {
        // attribute already selected
        log.warn("attribute {} already selected in a parent node", best.getAttr());
      }
  
      Node childNode;
      if (data.getDataset().isNumerical(best.getAttr())) {
        boolean[] temp = null;
  
        Data loSubset = data.subset(Condition.lesser(best.getAttr(), best.getSplit()));
        Data hiSubset = data.subset(Condition.greaterOrEquals(best.getAttr(), best.getSplit()));
  
        if (loSubset.isEmpty() || hiSubset.isEmpty()) {
          // the selected attribute did not change the data, avoid using it in the child notes
          selected[best.getAttr()] = true;
        } else {
          // the data changed, so we can unselect all previousely selected NUMERICAL attributes
          temp = selected;
          selected = cloneCategoricalAttributes(data.getDataset(), selected);
        }
  
        // size of the subset is less than the minSpitNum
        if (loSubset.size() < minSplitNum || hiSubset.size() < minSplitNum) {
          // branch is not split
          double label;
          if (data.getDataset().isNumerical(data.getDataset().getLabelId())) {
            label = sum / data.size();
          } else {
            label = data.majorityLabel(rng);
          }
          log.debug("branch is not split Leaf({})", label);
          return new Leaf(label);
        }
  
        Node loChild = build(rng, loSubset);
        Node hiChild = build(rng, hiSubset);
  
        // restore the selection state of the attributes
        if (temp != null) {
          selected = temp;
        } else {
          selected[best.getAttr()] = alreadySelected;
        }
  
        childNode = new NumericalNode(best.getAttr(), best.getSplit(), loChild, hiChild);
      } else { // CATEGORICAL attribute
        double[] values = data.values(best.getAttr());
  
        // tree is complemented
        Collection<Double> subsetValues = null;
        if (complemented) {
          subsetValues = new HashSet<>();
          for (double value : values) {
            subsetValues.add(value);
          }
  