/*
* Copyright (c) 2010-2012 Grid Dynamics Consulting Services, Inc, All Rights Reserved
* http://www.griddynamics.com
*
* This library is free software; you can redistribute it and/or modify it under the terms of
* the Apache License; either
* version 2.0 of the License, or any later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.griddynamics.jagger.util.statistics.percentiles;
import java.util.ArrayList;
// This class is a derivation of hist4j https://github.com/flaptor/hist4j
/**
* 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 implements HistogramNode {
// Attributes of a fork node.
private double 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 (double 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, double 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 (double 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 (double 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 Double getValueForAccumCount (long[] accumCount) {
Double 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);
}
/**
* 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);
}
}