protected TreeNode createNode(Dataset dataset, int limit, double[] stats) {
		boolean stdIs0 = getStats(dataset.instances, stats);
		final double totalWeights = stats[0];
		final double sum = stats[1];
		final double weightedMean = stats[2];

		// 1. Check basic leaf conditions
		if (dataset.instances.size() < limit || stdIs0) {
			TreeNode node = new RegressionTreeLeaf(weightedMean);
			return node;
		}

		// 2. Find best split
		double bestEval = Double.POSITIVE_INFINITY;
		List<IntDoublePair> splits = new ArrayList<>();
		List<Attribute> attributes = dataset.instances.getAttributes();
		for (int j = 0; j < attributes.size(); j++) {
			int attIndex = attributes.get(j).getIndex();
			List<IntDoublePair> sortedList = dataset.sortedLists.get(j);
			List<Double> uniqueValues = new ArrayList<>(sortedList.size());
			List<DoublePair> histogram = new ArrayList<>(sortedList.size());
			getHistogram(dataset.instances, sortedList, uniqueValues, totalWeights, sum, histogram);

			if (uniqueValues.size() > 1) {
				DoublePair split = split(uniqueValues, histogram, totalWeights, sum);
				if (split.v2 <= bestEval) {
					IntDoublePair splitPoint = new IntDoublePair(attIndex, split.v1);
					if (split.v2 < bestEval) {
						splits.clear();
						bestEval = split.v2;
					}
					splits.add(splitPoint);
				}
			}
		}
		if (bestEval < Double.POSITIVE_INFINITY) {
			Random rand = Random.getInstance();
			IntDoublePair splitPoint = splits.get(rand.nextInt(splits.size()));
			int attIndex = splitPoint.v1;
			TreeNode node = new TreeInteriorNode(attIndex, splitPoint.v2);
			stats[3] = bestEval + totalWeights * weightedMean * weightedMean;
			return node;
		} else {
			TreeNode node = new RegressionTreeLeaf(weightedMean);
			return node;
		}
	}