1fb3fb4f3Stomee /*
2fb3fb4f3Stomee  * CDDL HEADER START
3fb3fb4f3Stomee  *
4fb3fb4f3Stomee  * The contents of this file are subject to the terms of the
5fb3fb4f3Stomee  * Common Development and Distribution License (the "License").
6fb3fb4f3Stomee  * You may not use this file except in compliance with the License.
7fb3fb4f3Stomee  *
8fb3fb4f3Stomee  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fb3fb4f3Stomee  * or http://www.opensolaris.org/os/licensing.
10fb3fb4f3Stomee  * See the License for the specific language governing permissions
11fb3fb4f3Stomee  * and limitations under the License.
12fb3fb4f3Stomee  *
13fb3fb4f3Stomee  * When distributing Covered Code, include this CDDL HEADER in each
14fb3fb4f3Stomee  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fb3fb4f3Stomee  * If applicable, add the following below this CDDL HEADER, with the
16fb3fb4f3Stomee  * fields enclosed by brackets "[]" replaced with your own identifying
17fb3fb4f3Stomee  * information: Portions Copyright [yyyy] [name of copyright owner]
18fb3fb4f3Stomee  *
19fb3fb4f3Stomee  * CDDL HEADER END
20fb3fb4f3Stomee  */
21fb3fb4f3Stomee 
22fb3fb4f3Stomee /*
23*e77b06d2Stomee  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24fb3fb4f3Stomee  * Use is subject to license terms.
25fb3fb4f3Stomee  *
26fb3fb4f3Stomee  * ident	"%Z%%M%	%I%	%E% SMI"
27fb3fb4f3Stomee  */
28fb3fb4f3Stomee package org.opensolaris.os.dtrace;
29fb3fb4f3Stomee 
30fb3fb4f3Stomee import java.util.*;
31fb3fb4f3Stomee import java.io.*;
32fb3fb4f3Stomee import java.beans.*;
33fb3fb4f3Stomee 
34fb3fb4f3Stomee /**
35fb3fb4f3Stomee  * A frequency distribution aggregated by the DTrace {@code quantize()}
36fb3fb4f3Stomee  * or {@code lquantize()} action.  Each aggregated value falls into a
37fb3fb4f3Stomee  * range known as a bucket and counts toward the frequency of that
38fb3fb4f3Stomee  * bucket.  Bucket ranges are consecutive, with the maximum of one
39fb3fb4f3Stomee  * bucket's range always one less than the minimum of the next bucket's
40fb3fb4f3Stomee  * range.  By convention each bucket is identified by the minimum of its
41fb3fb4f3Stomee  * range.
42fb3fb4f3Stomee  *
43fb3fb4f3Stomee  * @author Tom Erickson
44fb3fb4f3Stomee  */
45fb3fb4f3Stomee public abstract class Distribution implements AggregationValue,
46fb3fb4f3Stomee        Iterable <Distribution.Bucket>, Serializable
47fb3fb4f3Stomee {
48fb3fb4f3Stomee     static final long serialVersionUID = 1186243118882654932L;
49fb3fb4f3Stomee 
50fb3fb4f3Stomee     /** @serial */
51fb3fb4f3Stomee     private List <Bucket> buckets;
52fb3fb4f3Stomee     private transient double total;
53fb3fb4f3Stomee     private transient boolean initialized;
54fb3fb4f3Stomee 
55fb3fb4f3Stomee     /**
56fb3fb4f3Stomee      * Package level access, called by subclasses LinearDistribution and
57fb3fb4f3Stomee      * LogDistribution, but not available outside the API.
58fb3fb4f3Stomee      *
59fb3fb4f3Stomee      * @param base  the lower bound of this distribution, or zero if not
60fb3fb4f3Stomee      * applicable
61fb3fb4f3Stomee      * @param constant  the constant term of the distribution function
62fb3fb4f3Stomee      * used to calculate the lower bound of any bucket given the lower
63fb3fb4f3Stomee      * bound of the previous bucket, for example the step in a linear
64fb3fb4f3Stomee      * distribution or the log base in a logarithmic distribution.
65fb3fb4f3Stomee      * @param frequencies  for each bucket, the number of aggregated
66fb3fb4f3Stomee      * values falling into that bucket's range; each element must be a
67fb3fb4f3Stomee      * positive integer
68fb3fb4f3Stomee      * @throws NullPointerException if frequencies is null
69fb3fb4f3Stomee      * @throws IllegalArgumentException if any element of frequencies
70fb3fb4f3Stomee      * does not have the expected range as defined by checkBucketRange()
71fb3fb4f3Stomee      */
Distribution(long base, long constant, long[] frequencies)72fb3fb4f3Stomee     Distribution(long base, long constant, long[] frequencies)
73fb3fb4f3Stomee     {
74fb3fb4f3Stomee 	total = 0;
75fb3fb4f3Stomee 	long frequency;
76fb3fb4f3Stomee 	for (int i = 0, len = frequencies.length; i < len; ++i) {
77fb3fb4f3Stomee 	    frequency = frequencies[i];
78fb3fb4f3Stomee 	    total += frequency;
79fb3fb4f3Stomee 	}
80fb3fb4f3Stomee 
81fb3fb4f3Stomee 	buckets = createBuckets(base, constant, frequencies);
82fb3fb4f3Stomee 	initialized = true;
83fb3fb4f3Stomee     }
84fb3fb4f3Stomee 
85fb3fb4f3Stomee     /**
86fb3fb4f3Stomee      * Supports XML persistence of subclasses.  Sub-class implementation
87fb3fb4f3Stomee      * must call initialize() after setting any state specific to that
88fb3fb4f3Stomee      * subclass for determining bucket ranges.
89fb3fb4f3Stomee      *
90fb3fb4f3Stomee      * @throws NullPointerException if frequencies is null
91fb3fb4f3Stomee      * @throws IllegalArgumentException if any element of frequencies
92fb3fb4f3Stomee      * does not have the expected range as defined by checkBucketRange()
93fb3fb4f3Stomee      */
Distribution(List <Bucket> frequencies)94fb3fb4f3Stomee     Distribution(List <Bucket> frequencies)
95fb3fb4f3Stomee     {
96fb3fb4f3Stomee 	// defensively copy frequencies list
97fb3fb4f3Stomee 	int len = frequencies.size();
98fb3fb4f3Stomee 	// don't need gratuitous capacity % added by constructor that
99fb3fb4f3Stomee 	// takes a Collection argument; list will not be modified
100fb3fb4f3Stomee 	buckets = new ArrayList <Bucket> (len);
101fb3fb4f3Stomee 	buckets.addAll(frequencies);
102fb3fb4f3Stomee     }
103fb3fb4f3Stomee 
104fb3fb4f3Stomee     final void
initialize()105fb3fb4f3Stomee     initialize()
106fb3fb4f3Stomee     {
107fb3fb4f3Stomee         // Called by constructor and readObject() (deserialization).
108fb3fb4f3Stomee 	// 1. Check class invariants, throw exception if deserialized
109fb3fb4f3Stomee 	//    state inconsistent with a Distribution that can result
110fb3fb4f3Stomee 	//    from the public constructor.
111fb3fb4f3Stomee 	// 2. Compute total (transient property derived from buckets)
112fb3fb4f3Stomee 	total = 0;
113fb3fb4f3Stomee 	long frequency;
114fb3fb4f3Stomee 	Bucket bucket;
115fb3fb4f3Stomee 	int len = buckets.size();
116fb3fb4f3Stomee 	for (int i = 0; i < len; ++i) {
117fb3fb4f3Stomee 	    bucket = buckets.get(i);
118fb3fb4f3Stomee 	    frequency = bucket.getFrequency();
119fb3fb4f3Stomee 	    // relies on package-private getBucketRange()
120fb3fb4f3Stomee 	    // implementation
121fb3fb4f3Stomee 	    checkBucketRange(i, len, bucket);
122fb3fb4f3Stomee 	    total += frequency;
123fb3fb4f3Stomee 	}
124fb3fb4f3Stomee 	initialized = true;
125fb3fb4f3Stomee     }
126fb3fb4f3Stomee 
127fb3fb4f3Stomee     // Must be called by public instance methods (since the AbstractList
128fb3fb4f3Stomee     // methods all depend on get() and size(), it is sufficient to call
129fb3fb4f3Stomee     // checkInit() only in those inherited methods).
130fb3fb4f3Stomee     private void
checkInit()131fb3fb4f3Stomee     checkInit()
132fb3fb4f3Stomee     {
133fb3fb4f3Stomee 	if (!initialized) {
134fb3fb4f3Stomee 	    throw new IllegalStateException("Uninitialized");
135fb3fb4f3Stomee 	}
136fb3fb4f3Stomee     }
137fb3fb4f3Stomee 
138fb3fb4f3Stomee     /**
139fb3fb4f3Stomee      * Gets a two element array: the first elelemt is the range minimum
140fb3fb4f3Stomee      * (inclusive), the second element is the range maximum (inclusive).
141fb3fb4f3Stomee      * Implemented by subclasses LinearDistribution and LogDistribution
142fb3fb4f3Stomee      * to define bucket ranges for this distribution and not available
143fb3fb4f3Stomee      * outside the API.  Used by the private general purpose constructor
144fb3fb4f3Stomee      * called from native code.  Implementation must not use
145fb3fb4f3Stomee      * subclass-specific state, since subclass state has not yet been
146fb3fb4f3Stomee      * allocated.
147fb3fb4f3Stomee      *
148fb3fb4f3Stomee      * @see #Distribution(long base, long constant, long[] frequencies)
149fb3fb4f3Stomee      */
150fb3fb4f3Stomee     abstract long[]
getBucketRange(int i, int len, long base, long constant)151fb3fb4f3Stomee     getBucketRange(int i, int len, long base, long constant);
152fb3fb4f3Stomee 
153fb3fb4f3Stomee     /**
154fb3fb4f3Stomee      * Used by public constructors and deserialization only after
155fb3fb4f3Stomee      * state specific to the subclass is available to the method.
156fb3fb4f3Stomee      */
157fb3fb4f3Stomee     abstract long[]
getBucketRange(int i, int len)158fb3fb4f3Stomee     getBucketRange(int i, int len);
159fb3fb4f3Stomee 
160fb3fb4f3Stomee     private List <Distribution.Bucket>
createBuckets(long base, long constant, long[] frequencies)161fb3fb4f3Stomee     createBuckets(long base, long constant, long[] frequencies)
162fb3fb4f3Stomee     {
163fb3fb4f3Stomee 	int len = frequencies.length;
164fb3fb4f3Stomee 	Bucket bucket;
165fb3fb4f3Stomee 	List <Bucket> buckets = new ArrayList <Bucket> (len);
166fb3fb4f3Stomee 	long min; // current bucket
167fb3fb4f3Stomee 	long max; // next bucket minus one
168fb3fb4f3Stomee 	long[] range; // two element array: { min, max }
169fb3fb4f3Stomee 
170fb3fb4f3Stomee 	for (int i = 0; i < len; ++i) {
171fb3fb4f3Stomee 	    range = getBucketRange(i, len, base, constant);
172fb3fb4f3Stomee 	    min = range[0];
173fb3fb4f3Stomee 	    max = range[1];
174fb3fb4f3Stomee 	    bucket = new Distribution.Bucket(min, max, frequencies[i]);
175fb3fb4f3Stomee 	    buckets.add(bucket);
176fb3fb4f3Stomee 	}
177fb3fb4f3Stomee 
178fb3fb4f3Stomee 	return buckets;
179fb3fb4f3Stomee     }
180fb3fb4f3Stomee 
181fb3fb4f3Stomee     /**
182fb3fb4f3Stomee      * Validates that bucket has the expected range for the given bucket
183fb3fb4f3Stomee      * index.  Uses {@code base} and {@code constant} constructor args
184fb3fb4f3Stomee      * to check invariants specific to each subclass, since state
185fb3fb4f3Stomee      * derived from these args in a subclass is not yet available in the
186fb3fb4f3Stomee      * superclass constructor.
187fb3fb4f3Stomee      *
188fb3fb4f3Stomee      * @throws IllegalArgumentException if bucket does not have the
189fb3fb4f3Stomee      * expected range for the given bucket index {@code i}
190fb3fb4f3Stomee      */
191fb3fb4f3Stomee     private void
checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket, long base, long constant)192fb3fb4f3Stomee     checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket,
193fb3fb4f3Stomee 	    long base, long constant)
194fb3fb4f3Stomee     {
195fb3fb4f3Stomee 	long[] range = getBucketRange(i, bucketCount, base, constant);
196fb3fb4f3Stomee 	checkBucketRange(i, bucket, range);
197fb3fb4f3Stomee     }
198fb3fb4f3Stomee 
199fb3fb4f3Stomee     private void
checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket)200fb3fb4f3Stomee     checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket)
201fb3fb4f3Stomee     {
202fb3fb4f3Stomee 	long[] range = getBucketRange(i, bucketCount);
203fb3fb4f3Stomee 	checkBucketRange(i, bucket, range);
204fb3fb4f3Stomee     }
205fb3fb4f3Stomee 
206fb3fb4f3Stomee     private void
checkBucketRange(int i, Distribution.Bucket bucket, long[] range)207fb3fb4f3Stomee     checkBucketRange(int i, Distribution.Bucket bucket, long[] range)
208fb3fb4f3Stomee     {
209fb3fb4f3Stomee 	long min = range[0];
210fb3fb4f3Stomee 	long max = range[1];
211fb3fb4f3Stomee 
212fb3fb4f3Stomee 	if (bucket.getMin() != min) {
213fb3fb4f3Stomee 	    throw new IllegalArgumentException("bucket min " +
214fb3fb4f3Stomee 		    bucket.getMin() + " at index " + i + ", expected " + min);
215fb3fb4f3Stomee 	}
216fb3fb4f3Stomee 	if (bucket.getMax() != max) {
217fb3fb4f3Stomee 	    throw new IllegalArgumentException("bucket max " +
218fb3fb4f3Stomee 		    bucket.getMax() + " at index " + i + ", expected " + max);
219fb3fb4f3Stomee 	}
220fb3fb4f3Stomee     }
221fb3fb4f3Stomee 
222fb3fb4f3Stomee     /**
223fb3fb4f3Stomee      * Gets a modifiable list of this distribution's buckets ordered by
224fb3fb4f3Stomee      * bucket range.  Modifying the returned list has no effect on this
225fb3fb4f3Stomee      * distribution.  Supports XML persistence.
226fb3fb4f3Stomee      *
227fb3fb4f3Stomee      * @return a modifiable list of this distribution's buckets ordered
228fb3fb4f3Stomee      * by bucket range
229fb3fb4f3Stomee      */
230fb3fb4f3Stomee     public List <Bucket>
getBuckets()231fb3fb4f3Stomee     getBuckets()
232fb3fb4f3Stomee     {
233fb3fb4f3Stomee 	checkInit();
234fb3fb4f3Stomee 	return new ArrayList <Bucket> (buckets);
235fb3fb4f3Stomee     }
236fb3fb4f3Stomee 
237fb3fb4f3Stomee     /**
238*e77b06d2Stomee      * Gets a read-only {@code List} view of this distribution.
239fb3fb4f3Stomee      *
240*e77b06d2Stomee      * @return a read-only {@code List} view of this distribution
241fb3fb4f3Stomee      */
242fb3fb4f3Stomee     public List <Bucket>
asList()243fb3fb4f3Stomee     asList()
244fb3fb4f3Stomee     {
245fb3fb4f3Stomee 	checkInit();
246*e77b06d2Stomee 	return Collections. <Bucket> unmodifiableList(buckets);
247fb3fb4f3Stomee     }
248fb3fb4f3Stomee 
249fb3fb4f3Stomee     /**
250fb3fb4f3Stomee      * Gets the number of buckets in this distribution.
251fb3fb4f3Stomee      *
252fb3fb4f3Stomee      * @return non-negative bucket count
253fb3fb4f3Stomee      */
254fb3fb4f3Stomee     public int
size()255fb3fb4f3Stomee     size()
256fb3fb4f3Stomee     {
257fb3fb4f3Stomee 	checkInit();
258fb3fb4f3Stomee 	return buckets.size();
259fb3fb4f3Stomee     }
260fb3fb4f3Stomee 
261fb3fb4f3Stomee     /**
262fb3fb4f3Stomee      * Gets the bucket at the given distribution index (starting at
263fb3fb4f3Stomee      * zero).
264fb3fb4f3Stomee      *
265fb3fb4f3Stomee      * @return non-null distribution bucket at the given zero-based
266fb3fb4f3Stomee      * index
267fb3fb4f3Stomee      */
268fb3fb4f3Stomee     public Bucket
get(int index)269fb3fb4f3Stomee     get(int index)
270fb3fb4f3Stomee     {
271fb3fb4f3Stomee 	checkInit();
272fb3fb4f3Stomee 	return buckets.get(index);
273fb3fb4f3Stomee     }
274fb3fb4f3Stomee 
275fb3fb4f3Stomee     /**
276fb3fb4f3Stomee      * Gets an iterator over the buckets of this distribution.
277fb3fb4f3Stomee      *
278fb3fb4f3Stomee      * @return an iterator over the buckets of this distribution
279fb3fb4f3Stomee      */
280fb3fb4f3Stomee     public Iterator<Bucket>
iterator()281fb3fb4f3Stomee     iterator()
282fb3fb4f3Stomee     {
283fb3fb4f3Stomee 	checkInit();
284fb3fb4f3Stomee 	return buckets.iterator();
285fb3fb4f3Stomee     }
286fb3fb4f3Stomee 
287fb3fb4f3Stomee     /**
288fb3fb4f3Stomee      * Compares the specified object with this {@code Distribution}
289fb3fb4f3Stomee      * instance for equality.  Defines equality as having the same
290fb3fb4f3Stomee      * buckets with the same values.
291fb3fb4f3Stomee      *
292fb3fb4f3Stomee      * @return {@code true} if and only if the specified object is of
293fb3fb4f3Stomee      * type {@code Distribution} and both instances have the same size
294fb3fb4f3Stomee      * and equal buckets at corresponding distribution indexes
295fb3fb4f3Stomee      */
296fb3fb4f3Stomee     public boolean
equals(Object o)297fb3fb4f3Stomee     equals(Object o)
298fb3fb4f3Stomee     {
299fb3fb4f3Stomee 	checkInit();
300fb3fb4f3Stomee 	if (o instanceof Distribution) {
301fb3fb4f3Stomee 	    Distribution d = (Distribution)o;
302fb3fb4f3Stomee 	    return buckets.equals(d.buckets);
303fb3fb4f3Stomee 	}
304fb3fb4f3Stomee 	return false;
305fb3fb4f3Stomee     }
306fb3fb4f3Stomee 
307fb3fb4f3Stomee     /**
308fb3fb4f3Stomee      * Overridden to ensure that equals instances have equal hash codes.
309fb3fb4f3Stomee      */
310fb3fb4f3Stomee     public int
hashCode()311fb3fb4f3Stomee     hashCode()
312fb3fb4f3Stomee     {
313fb3fb4f3Stomee 	checkInit();
314fb3fb4f3Stomee 	return buckets.hashCode();
315fb3fb4f3Stomee     }
316fb3fb4f3Stomee 
317fb3fb4f3Stomee     /**
318fb3fb4f3Stomee      * Gets the total frequency across all buckets.
319fb3fb4f3Stomee      *
320fb3fb4f3Stomee      * @return sum of the frequency of all buckets in this distribution
321fb3fb4f3Stomee      */
322fb3fb4f3Stomee     public double
getTotal()323fb3fb4f3Stomee     getTotal()
324fb3fb4f3Stomee     {
325fb3fb4f3Stomee 	checkInit();
326fb3fb4f3Stomee 	return total;
327fb3fb4f3Stomee     }
328fb3fb4f3Stomee 
329fb3fb4f3Stomee     /**
330fb3fb4f3Stomee      * Gets the numeric value of this distribution used to compare
331fb3fb4f3Stomee      * distributions by overall magnitude, defined as the sum total of
332fb3fb4f3Stomee      * each bucket's frequency times the minimum of its range.
333fb3fb4f3Stomee      */
getValue()334fb3fb4f3Stomee     public abstract Number getValue();
335fb3fb4f3Stomee 
336fb3fb4f3Stomee     /**
337fb3fb4f3Stomee      * Called by native code
338fb3fb4f3Stomee      */
339fb3fb4f3Stomee     private void
normalizeBuckets(long normal)340fb3fb4f3Stomee     normalizeBuckets(long normal)
341fb3fb4f3Stomee     {
342fb3fb4f3Stomee 	for (Bucket b : buckets) {
343fb3fb4f3Stomee 	    b.frequency /= normal;
344fb3fb4f3Stomee 	}
345fb3fb4f3Stomee     }
346fb3fb4f3Stomee 
347fb3fb4f3Stomee     /**
348fb3fb4f3Stomee      * A range inclusive at both endpoints and a count of aggregated
349fb3fb4f3Stomee      * values that fall in that range.  Buckets in a {@link
350fb3fb4f3Stomee      * Distribution} are consecutive, such that the max of one bucket is
351fb3fb4f3Stomee      * always one less than the min of the next bucket (or {@link
352fb3fb4f3Stomee      * Long#MAX_VALUE} if it is the last bucket in the {@code
353fb3fb4f3Stomee      * Distribution}).
354fb3fb4f3Stomee      * <p>
355fb3fb4f3Stomee      * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
356fb3fb4f3Stomee      */
357fb3fb4f3Stomee     public static final class Bucket implements Serializable {
358fb3fb4f3Stomee 	static final long serialVersionUID = 4863264115375406295L;
359fb3fb4f3Stomee 
360fb3fb4f3Stomee 	/** @serial */
361fb3fb4f3Stomee 	private final long min;
362fb3fb4f3Stomee 	/** @serial */
363fb3fb4f3Stomee 	private final long max;
364fb3fb4f3Stomee 	/** @serial */
365fb3fb4f3Stomee 	private long frequency; // non-final so native code can normalize
366fb3fb4f3Stomee 
367fb3fb4f3Stomee 	static {
368fb3fb4f3Stomee 	    try {
369fb3fb4f3Stomee 		BeanInfo info = Introspector.getBeanInfo(Bucket.class);
370fb3fb4f3Stomee 		PersistenceDelegate persistenceDelegate =
371fb3fb4f3Stomee 			new DefaultPersistenceDelegate(
372fb3fb4f3Stomee 			new String[] {"min", "max", "frequency"})
373fb3fb4f3Stomee 		{
374fb3fb4f3Stomee 		    /*
375fb3fb4f3Stomee 		     * Need to prevent DefaultPersistenceDelegate from using
376fb3fb4f3Stomee 		     * overridden equals() method, resulting in a
377fb3fb4f3Stomee 		     * StackOverFlowError.  Revert to PersistenceDelegate
378fb3fb4f3Stomee 		     * implementation.  See
379fb3fb4f3Stomee 		     * http://forum.java.sun.com/thread.jspa?threadID=
380fb3fb4f3Stomee 		     * 477019&tstart=135
381fb3fb4f3Stomee 		     */
382fb3fb4f3Stomee 		    protected boolean
383fb3fb4f3Stomee 		    mutatesTo(Object oldInstance, Object newInstance)
384fb3fb4f3Stomee 		    {
385fb3fb4f3Stomee 			return (newInstance != null && oldInstance != null &&
386fb3fb4f3Stomee 				(oldInstance.getClass() ==
387fb3fb4f3Stomee 				newInstance.getClass()));
388fb3fb4f3Stomee 		    }
389fb3fb4f3Stomee 		};
390fb3fb4f3Stomee 		BeanDescriptor d = info.getBeanDescriptor();
391fb3fb4f3Stomee 		d.setValue("persistenceDelegate", persistenceDelegate);
392fb3fb4f3Stomee 	    } catch (IntrospectionException e) {
393fb3fb4f3Stomee 		System.out.println(e);
394fb3fb4f3Stomee 	    }
395fb3fb4f3Stomee 	}
396fb3fb4f3Stomee 
397fb3fb4f3Stomee 	/**
398fb3fb4f3Stomee 	 * Creates a distribution bucket with the given range and
399fb3fb4f3Stomee 	 * frequency.
400fb3fb4f3Stomee 	 *
401fb3fb4f3Stomee 	 * @param rangeMinimumInclusive sets the lower bound (inclusive)
402fb3fb4f3Stomee 	 * returned by {@link #getMin()}
403fb3fb4f3Stomee 	 * @param rangeMaximumInclusive sets the upper bound (inclusive)
404fb3fb4f3Stomee 	 * returned by {@link #getMax()}
405fb3fb4f3Stomee 	 * @param valuesInRange sets the value frequency in this
406fb3fb4f3Stomee 	 * bucket's range returned by {@link #getFrequency()}
407fb3fb4f3Stomee 	 * @throws IllegalArgumentException if {@code
408fb3fb4f3Stomee 	 * rangeMaximumInclusive} is less than {@code
409fb3fb4f3Stomee 	 * rangeMinimumInclusive}
410fb3fb4f3Stomee 	 */
411fb3fb4f3Stomee 	public
Bucket(long rangeMinimumInclusive, long rangeMaximumInclusive, long valuesInRange)412fb3fb4f3Stomee 	Bucket(long rangeMinimumInclusive, long rangeMaximumInclusive,
413fb3fb4f3Stomee 		long valuesInRange)
414fb3fb4f3Stomee 	{
415fb3fb4f3Stomee 	    if (rangeMaximumInclusive < rangeMinimumInclusive) {
416fb3fb4f3Stomee 		throw new IllegalArgumentException("upper bound " +
417fb3fb4f3Stomee 			rangeMaximumInclusive + " is less than lower bound " +
418fb3fb4f3Stomee 			rangeMinimumInclusive);
419fb3fb4f3Stomee 	    }
420fb3fb4f3Stomee 
421fb3fb4f3Stomee 	    min = rangeMinimumInclusive;
422fb3fb4f3Stomee 	    max = rangeMaximumInclusive;
423fb3fb4f3Stomee 	    frequency = valuesInRange;
424fb3fb4f3Stomee 	}
425fb3fb4f3Stomee 
426fb3fb4f3Stomee 	/**
427fb3fb4f3Stomee 	 * Gets the lower bound of this bucket's range (inclusive).
428fb3fb4f3Stomee 	 */
429fb3fb4f3Stomee 	public long
getMin()430fb3fb4f3Stomee 	getMin()
431fb3fb4f3Stomee 	{
432fb3fb4f3Stomee 	    return min;
433fb3fb4f3Stomee 	}
434fb3fb4f3Stomee 
435fb3fb4f3Stomee 	/**
436fb3fb4f3Stomee 	 * Gets the upper bound of this bucket's range (inclusive).
437fb3fb4f3Stomee 	 */
438fb3fb4f3Stomee 	public long
getMax()439fb3fb4f3Stomee 	getMax()
440fb3fb4f3Stomee 	{
441fb3fb4f3Stomee 	    return max;
442fb3fb4f3Stomee 	}
443fb3fb4f3Stomee 
444fb3fb4f3Stomee 	/**
445fb3fb4f3Stomee 	 * Gets the number of values in a {@link Distribution} that fall
446fb3fb4f3Stomee 	 * into the range defined by this bucket.
447fb3fb4f3Stomee 	 */
448fb3fb4f3Stomee 	public long
getFrequency()449fb3fb4f3Stomee 	getFrequency()
450fb3fb4f3Stomee 	{
451fb3fb4f3Stomee 	    return frequency;
452fb3fb4f3Stomee 	}
453fb3fb4f3Stomee 
454fb3fb4f3Stomee 	/**
455fb3fb4f3Stomee 	 * Compares the specified object with this distribution bucket
456fb3fb4f3Stomee 	 * for equality.  Defines equality of two distribution buckets
457fb3fb4f3Stomee 	 * as having the same range and the same frequency.
458fb3fb4f3Stomee 	 *
459fb3fb4f3Stomee 	 * @return false if the specified object is not a {@code
460fb3fb4f3Stomee 	 * Distribution.Bucket}
461fb3fb4f3Stomee 	 */
462fb3fb4f3Stomee 	@Override
463fb3fb4f3Stomee 	public boolean
equals(Object o)464fb3fb4f3Stomee 	equals(Object o)
465fb3fb4f3Stomee 	{
466fb3fb4f3Stomee 	    if (o instanceof Bucket) {
467fb3fb4f3Stomee 		Bucket b = (Bucket)o;
468fb3fb4f3Stomee 		return ((min == b.min) &&
469fb3fb4f3Stomee 			(max == b.max) &&
470fb3fb4f3Stomee 			(frequency == b.frequency));
471fb3fb4f3Stomee 	    }
472fb3fb4f3Stomee 	    return false;
473fb3fb4f3Stomee 	}
474fb3fb4f3Stomee 
475fb3fb4f3Stomee 	/**
476fb3fb4f3Stomee 	 * Overridden to ensure that equal buckets have equal hashcodes.
477fb3fb4f3Stomee 	 */
478fb3fb4f3Stomee 	@Override
479fb3fb4f3Stomee 	public int
hashCode()480fb3fb4f3Stomee 	hashCode()
481fb3fb4f3Stomee 	{
482fb3fb4f3Stomee 	    int hash = 17;
483fb3fb4f3Stomee 	    hash = (37 * hash) + ((int)(min ^ (min >>> 32)));
484fb3fb4f3Stomee 	    hash = (37 * hash) + ((int)(max ^ (max >>> 32)));
485fb3fb4f3Stomee 	    hash = (37 * hash) + ((int)(frequency ^ (frequency >>> 32)));
486fb3fb4f3Stomee 	    return hash;
487fb3fb4f3Stomee 	}
488fb3fb4f3Stomee 
489fb3fb4f3Stomee 	private void
readObject(ObjectInputStream s)490fb3fb4f3Stomee 	readObject(ObjectInputStream s)
491fb3fb4f3Stomee 		throws IOException, ClassNotFoundException
492fb3fb4f3Stomee 	{
493fb3fb4f3Stomee 	    s.defaultReadObject();
494fb3fb4f3Stomee 	    // check class invariants (as constructor does)
495fb3fb4f3Stomee 	    if (max < min) {
496fb3fb4f3Stomee 		throw new InvalidObjectException("upper bound " +
497fb3fb4f3Stomee 			max + " is less than lower bound " + min);
498fb3fb4f3Stomee 	    }
499fb3fb4f3Stomee 	}
500fb3fb4f3Stomee 
501fb3fb4f3Stomee 	/**
502fb3fb4f3Stomee 	 * Gets a string representation of this distribution bucket
503fb3fb4f3Stomee 	 * useful for logging and not intended for display.  The exact
504fb3fb4f3Stomee 	 * details of the representation are unspecified and subject to
505fb3fb4f3Stomee 	 * change, but the following format may be regarded as typical:
506fb3fb4f3Stomee 	 * <pre><code>
507fb3fb4f3Stomee 	 * class-name[property1 = value1, property2 = value2]
508fb3fb4f3Stomee 	 * </code></pre>
509fb3fb4f3Stomee 	 */
510fb3fb4f3Stomee 	public String
toString()511fb3fb4f3Stomee 	toString()
512fb3fb4f3Stomee 	{
5134ae67516Stomee 	    StringBuilder buf = new StringBuilder();
514fb3fb4f3Stomee 	    buf.append(Bucket.class.getName());
515fb3fb4f3Stomee 	    buf.append("[min = ");
516fb3fb4f3Stomee 	    buf.append(min);
517fb3fb4f3Stomee 	    buf.append(", max = ");
518fb3fb4f3Stomee 	    buf.append(max);
519fb3fb4f3Stomee 	    buf.append(", frequency = ");
520fb3fb4f3Stomee 	    buf.append(frequency);
521fb3fb4f3Stomee 	    buf.append(']');
522fb3fb4f3Stomee 	    return buf.toString();
523fb3fb4f3Stomee 	}
524fb3fb4f3Stomee     }
525fb3fb4f3Stomee 
526fb3fb4f3Stomee     /**
527fb3fb4f3Stomee      * Gets a list of buckets of interest by excluding empty buckets at
528fb3fb4f3Stomee      * both ends of the distribution.  Leaves one empty bucket on each
529fb3fb4f3Stomee      * end if possible to convey the distribution context more
530fb3fb4f3Stomee      * effectively in a display.
531fb3fb4f3Stomee      *
532fb3fb4f3Stomee      * @return an unmodifiable sublist that includes the range starting
533fb3fb4f3Stomee      * from the first bucket with a non-zero frequency and ending with
534fb3fb4f3Stomee      * the last bucket with a non-zero frequency, plus one empty bucket
535fb3fb4f3Stomee      * before and after that range if possible
536fb3fb4f3Stomee      */
537fb3fb4f3Stomee     public List <Bucket>
getDisplayRange()538fb3fb4f3Stomee     getDisplayRange()
539fb3fb4f3Stomee     {
540fb3fb4f3Stomee 	checkInit();
541fb3fb4f3Stomee 	int min = -1;
542fb3fb4f3Stomee 	int max = -1;
543fb3fb4f3Stomee 	int len = size();
544fb3fb4f3Stomee 	Bucket b;
545fb3fb4f3Stomee 	// Get first non-empty bucket
546fb3fb4f3Stomee 	for (int i = 0; i < len; ++i) {
547fb3fb4f3Stomee 	    b = buckets.get(i);
548fb3fb4f3Stomee 	    if (b.getFrequency() > 0L) {
549fb3fb4f3Stomee 		min = i;
550fb3fb4f3Stomee 		break;
551fb3fb4f3Stomee 	    }
552fb3fb4f3Stomee 	}
553fb3fb4f3Stomee 	if (min < 0) {
554fb3fb4f3Stomee 	    return Collections. <Bucket> emptyList();
555fb3fb4f3Stomee 	}
556fb3fb4f3Stomee 	// Get last non-empty bucket
557fb3fb4f3Stomee 	for (int i = (len - 1); i >= 0; --i) {
558fb3fb4f3Stomee 	    b = buckets.get(i);
559fb3fb4f3Stomee 	    if (b.getFrequency() > 0L) {
560fb3fb4f3Stomee 		max = i;
561fb3fb4f3Stomee 		break;
562fb3fb4f3Stomee 	    }
563fb3fb4f3Stomee 	}
564fb3fb4f3Stomee 	// If possible, pad non-empty range with one empty bucket at
565fb3fb4f3Stomee 	// each end.
566fb3fb4f3Stomee 	if (min > 0) {
567fb3fb4f3Stomee 	    --min;
568fb3fb4f3Stomee 	}
569fb3fb4f3Stomee 	if (max < (len - 1)) {
570fb3fb4f3Stomee 	    ++max;
571fb3fb4f3Stomee 	}
572fb3fb4f3Stomee 
573fb3fb4f3Stomee 	// subList inclusive at low index, exclusive at high index
574fb3fb4f3Stomee 	return Collections. <Bucket>
575fb3fb4f3Stomee 		unmodifiableList(buckets).subList(min, max + 1);
576fb3fb4f3Stomee     }
577fb3fb4f3Stomee 
578fb3fb4f3Stomee     private void
readObject(ObjectInputStream s)579fb3fb4f3Stomee     readObject(ObjectInputStream s)
580fb3fb4f3Stomee             throws IOException, ClassNotFoundException
581fb3fb4f3Stomee     {
582fb3fb4f3Stomee 	s.defaultReadObject();
583fb3fb4f3Stomee 
584fb3fb4f3Stomee 	// Defensively copy buckets _before_ validating.  Subclass
585fb3fb4f3Stomee 	// validates by calling initialize() after reading any state
586fb3fb4f3Stomee 	// specific to that subclass needed for validation.
587fb3fb4f3Stomee 	int len = buckets.size();
588fb3fb4f3Stomee 	ArrayList <Bucket> copy = new ArrayList <Bucket> (len);
589fb3fb4f3Stomee 	copy.addAll(buckets);
590fb3fb4f3Stomee 	buckets = copy;
591fb3fb4f3Stomee     }
592fb3fb4f3Stomee 
593fb3fb4f3Stomee     /**
594fb3fb4f3Stomee      * Gets a string representation of this {@code Distribution} useful
595fb3fb4f3Stomee      * for logging and not intended for display.  The exact details of
596fb3fb4f3Stomee      * the representation are unspecified and subject to change, but the
597fb3fb4f3Stomee      * following format may be regarded as typical:
598fb3fb4f3Stomee      * <pre><code>
599fb3fb4f3Stomee      * class-name[property1 = value1, property2 = value2]
600fb3fb4f3Stomee      * </code></pre>
601fb3fb4f3Stomee      */
602fb3fb4f3Stomee     public String
toString()603fb3fb4f3Stomee     toString()
604fb3fb4f3Stomee     {
605fb3fb4f3Stomee 	checkInit();
6064ae67516Stomee 	StringBuilder buf = new StringBuilder();
607fb3fb4f3Stomee 	buf.append(Distribution.class.getName());
608fb3fb4f3Stomee 	buf.append("[buckets = ");
609fb3fb4f3Stomee 	List <Bucket> list = getDisplayRange();
610fb3fb4f3Stomee 	if (list.isEmpty()) {
611fb3fb4f3Stomee 	    buf.append("<empty>");
612fb3fb4f3Stomee 	} else {
613fb3fb4f3Stomee 	    buf.append(Arrays.toString(getDisplayRange().toArray()));
614fb3fb4f3Stomee 	}
615fb3fb4f3Stomee 	buf.append(", total = ");
616fb3fb4f3Stomee 	buf.append(getTotal());
617fb3fb4f3Stomee 	buf.append(']');
618fb3fb4f3Stomee 	return buf.toString();
619fb3fb4f3Stomee     }
620fb3fb4f3Stomee }
621