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