/* * Copyright 2007 Flaptor (flaptor.com) * * Licensed 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 com.flaptor.hist4j; import java.util.ArrayList; /** * The HistogramForkNode splits the data range in two at a given value, pointing to two subtrees, * one for values smaller than the split value, and one for values larger than the split value. * It implements the recursive calls necesary to obtain the data from the tree structure. * * @author Jorge Handl */ public class HistogramForkNode extends HistogramNode { private static final long serialVersionUID = -1L; // Attributes of a fork node. private float splitValue; private HistogramNode left = null; private HistogramNode right = null; /** * Creates a fork node with the given split value and subtrees. * * @param splitValue * the value that splits both subtrees. * @param left * the left subtree. * @param right * the right subtree. */ public HistogramForkNode(float splitValue, HistogramNode left, HistogramNode right) { this.splitValue = splitValue; this.left = left; this.right = right; } /** * Clears the fork node, recursively erasing the subtrees. */ public void reset() { if (null != left) { left.reset(); left = null; } if (null != right) { right.reset(); right = null; } splitValue = 0; } /** * Adds a value to the histogram by recursively adding the value to either subtree, depending on the split value. * * @param root * a reference to the adaptive histogram instance that uses this structure. * @param value * the value for which the count is to be incremented. * @return A reference to itself. */ public HistogramNode addValue(AdaptiveHistogram root, float value) { // The data node addValue implementation returns a reference to itself if there was no structural change needed, // or a reference to a new fork node if the data node had to be split in two. By assigning the returned reference // to the corresponding subtree variable (left or right), the subtree can replace itself with a new structure, // eliminating the need for a node to manipulate its subtree, for which it would need to know a lot about what // happens at the lower level. if (value > splitValue) { right = right.addValue(root, value); } else { left = left.addValue(root, value); } return this; } /** * Returns the number of data points stored in the same bucket as a given value. * * @param value * the reference data point. * @return the number of data points stored in the same bucket as the reference point. */ public long getCount(float value) { // The fork node recursively calls the appropriate subtree depending on the split value. long count = 0; if (value > splitValue) { count = right.getCount(value); } else { count = left.getCount(value); } return count; } /** * Returns the cumulative density function for a given data point. * * @param value * the reference data point. * @return the cumulative density function for the reference point. */ public long getAccumCount(float value) { // The fork node recursively calls the appropriate subtree depending on the split value. long count = left.getAccumCount(value); if (value > splitValue) { count += right.getAccumCount(value); } return count; } /** * Returns the data point where the running cumulative count reaches the target cumulative count. * * @param accumCount * accumCount[0] the running cumulative count. * accumCount[1] the target cumulative count. * @return the data point where the running cumulative count reaches the target cumulative count. */ public Float getValueForAccumCount(long[] accumCount) { Float val = left.getValueForAccumCount(accumCount); if (null == val) { val = right.getValueForAccumCount(accumCount); } return val; } /** * Applies a convertion function to the values stored in the histogram. * * @param valueConversion * a class that defines a function to convert the value. */ public void apply(AdaptiveHistogram.ValueConversion valueConversion) { left.apply(valueConversion); right.apply(valueConversion); splitValue = valueConversion.convertValue(splitValue); } /** * Prints the data for the nodes in its subtrees. * * @param level * the level of this node in the tree. */ public void show(int level) { left.show(level + 1); margin(level); System.out.println("Fork at: " + splitValue); right.show(level + 1); } /** * Build the table representing the histogram data adding the data from each subtree. */ public void toTable(ArrayList<Cell> table) { left.toTable(table); right.toTable(table); } }