/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * -- */ package com.sun.sgs.impl.profile.util; /** * A histogram that bins elements based on provided bounds and a step * size. This class is useful for viewing a particular range of data * and specifying how the distribution is grouped. The lower and * upper bounds should be used to filter out all extraneous data * points. */ public class LinearHistogram implements Histogram { /** * the longest bar in the histogram */ private static final int MAX_HIST_LENGTH = 40; /** * The inclusive lower bound for the histogram */ private final int lBound; /** * The inclusive upper bound for the histogram */ private final int uBound; /** * The integer range between buckets */ private final long stepSize; /** * The bins for the histogram, which contain the current count */ private final int[] bins; /** * The maximum index that contains an entry */ private int maxIndex; /** * The minimum index that contains an entry */ private int minIndex; /** * The maximum number of entries current in one bin of the * histogram. */ private int maxCount; /** * The number of samples that this histogram represents. */ private int size; /** * Creates a linear frequency histogram with the provided lower * bound, upper bound and step size. * * @param lBound the lower bound for the histogram; values * strictly less than this will not be included * @param uBound the upper bound for the histogram; values * strictly greater than this will not be included * @param stepSize the maximum range for each bin between {@code * lBound} and {@code uBound}; if {@code stepSize} does not * evenly divide the difference, the last bin will be * truncated in size * * @throws IllegalArgumentException if {@code lBound} is not less * than {@code uBound}, or if {@code stepSize} is not * strictly positive */ public LinearHistogram(int lBound, int uBound, long stepSize) { if (lBound >= uBound) { throw new IllegalArgumentException("Lower bound must be less " + "than upper bound"); } if (stepSize <= 0) { throw new IllegalArgumentException("Step size must be " + "strictly positive"); } this.lBound = lBound; this.uBound = uBound; this.stepSize = stepSize; bins = new int[(int) ((uBound - lBound) / stepSize) + 1]; maxIndex = Integer.MIN_VALUE; minIndex = Integer.MAX_VALUE; maxCount = 0; size = 0; } /** * {@inheritDoc} * * Values are binned in the largest bin that is <i>less than</i> * the element is incremented. Values less than the lower bound * or values that are greater than the upper bound are not counted. */ public void bin(long value) { if (value < lBound || value > uBound) { return; } // find the appropriate bin for this value, starting at the // lower bound and increasing by the provided step size int bin = 0; long i = lBound + stepSize; while (i < uBound) { i += stepSize; if (value > i) { bin++; } else { break; } } maxIndex = Math.max(maxIndex, bin); minIndex = Math.min(minIndex, bin); // keep track of which bin has the most number of elements, // after incrementing the bin's count int count = bins[bin]++; if (count > maxCount) { maxCount = count; } } /** * {@inheritDoc} */ public void clear() { for (int i = 0; i < bins.length; ++i) { bins[i] = 0; } maxIndex = Integer.MIN_VALUE; minIndex = Integer.MAX_VALUE; maxCount = 0; size = 0; } /** * {@inheritDoc} */ public int size() { return size; } /** * Generates a text representation of this linear frequency * histogram. The histogram is presented similar to the following * example: * * <pre> * [lower-bound] |*** * [l.b. + step] | * [l.b. + 2*step] |* * [upper-bound] |**** * </pre> * * Bins that have at least one sample will have a bar displayed. * All leading and trailing empty bins are truncated, which may * result in the lower and upper bound bins not being displayed. * The final line will have a newline at the end. * * @return a string representation of this histogram */ public String toString() { return toString(""); } /** * Generates a text representation of this linear frequency * histogram similar to {@link #toString()} but with labels on * bins. For example, a bin would appear as: * * <pre> * 200ms |*** * 400ms |* * 600ms |***** * </pre> * * @param binLabel the label to append to each of the bins * * @return a string representation of this histogram */ public String toString(String binLabel) { // get the length of the longest string version of the integer // to make the histogram line up correctly int maxLength = Long.toString(lBound + (maxIndex * stepSize)).length(); StringBuilder b = new StringBuilder(128); for (int i = minIndex; i <= maxIndex; ++i) { String binName = Long.toString(Math.min(lBound + (i * stepSize), uBound)); // make the bars all line up evenly by padding with spaces for (int j = binName.length(); j < maxLength; ++j) { b.append(" "); } b.append(binName).append("binLabel").append(" |"); // scale the bar length relative to the max int bars = (int) ((bins[i] / (double) maxCount) * MAX_HIST_LENGTH); // bump all non-empty buckets by one, so we can tell the // difference if (bins[i] > 0) { bars++; } for (int j = 0; j < bars; ++j) { b.append("*"); } b.append("\n"); } return b.toString(); } }