| Here and in the coming chapters we would like to add more options 
              to our histograms such as an option to store error values on each 
              bin. We could continue to create a new subclass for every new feature 
              that we discuss. This certainly offers step-by-step pedalogical 
              clarity. However, this would not provide for a sensible class heirarchy. In section Chapter 7: 
              Tech and this chapter we have already created three subclasses 
              of our Histogram 
              base class:  
              HistogramStat 
                - added calculation of the moments of the data.
HistogramAdapt 
                - added the option of saving each data value in an array and dynamically 
                recalculating the range of the histogram to include all data points. 
                
HistogramMedian 
                - calculated the median value of the histogram bins or directly 
                from the data if saved. Adding yet more subclasses will make the hierarchy of histogram 
              classes increasingly complex and awkward. Perhaps there are features 
              in the subclasses that logically belong in the base class or one 
              of the superclasses. This situation often occurs in object oriented 
              software development. As you think up new features and capabilities 
              and continually create new subclasses, the original class design 
              goals can become lost in a mess.  The alternative is to reorganize, or refactor as it is 
              now called, the classes to make them more conceptually clear, modular, 
              and understandable. (In Chapter 
              6: Tech : Hist Demo we refactored two early histogram classes 
              into the current Histogram 
              class.) Ideally, the refactoring of class structures will avoid, 
              or at least minimize, external changes so that constructors and 
              methods used by other classes will not "break" due to 
              these alterations and need re-coding as well. (Refactoring 
              is a big aspect of the so-called Extreme 
              Programming approach where it means to reorganize a class to 
              improve the design while keeping its external behavior unchanged.) Class design is still as much art as science. Knowing from the 
              beginning of a software project how to design the classes to allow 
              for straight-forward and elegant expansion of capabilities is quite 
              a tough challenge. Techniques such as design patterns (see 
              references) improve the situation but in the 
              course of almost any large programming project, many refactorings 
              of the classes will occur. Revisions One approach is simply to pack more and more methods and fields 
              into a bigger and bigger base class. This, however, makes for unwieldy 
              code that reduces modularity and conceptual clarity, defying the 
              whole purpose of OOP. (A practical drawback also comes when instances 
              of the class carry lots of fields that take up memory space but 
              are not used.)  On the other hand, a new subclass for every new feature is not 
              advisable either. Subclasses should offer unique capabilities for 
              the particular situations that demand those capabilities. The base 
              class should keep just those fields and methods needed in common 
              by all the subclasses. We could collapse all of the subclasses into one subclass but, 
              in the code discussed in this chapter, the adaptive 
              range subclass with its internal data array seems fundamentally 
              different from the statistics subclass. So we will create one new 
              base class and one new subclass, where the latter differs from the 
              former in its adaptive ranging. In normal practice we would modify, expand, and update Histogram 
              and its subclasses and try to do this internally to avoid upsetting 
              the programs that use these classes. And we would seek to keep the 
              subclasses distinct and the number of them to a minimum. However, 
              in this course we want to keep the class names unique to each particular 
              version for pedalogical clarity. So rather than modify an existing 
              class, we will create a new class but append R#, 
              as in HistogramStatR1 
              for HistogramStat, 
              Revision 1. 
             In our refactored Histogram 
              heirarchy we will keep Histogram 
              unchanged as our base class since it is compact and can provide 
              basic services. We will create a new version of HistogramStat 
              called HistogramStatR1. 
              It is similar to the earlier version but now provides a median calculated 
              from the histogram bins. Methods of particular interest include: 
              double [] getStats() 
                - this method overrides the one in Histogram 
                and provides an array of statistics , including median, calculated 
                from the contents of the bins. Underflows and overflows are ignored. 
                
double [] getDataStats() 
                - returns an array of statistics, including median, calculated 
                from the moments obtained during data entry. It uses all data 
                values, including those outside the range of the histogram.
getMedian() 
                - estimates the median from the histogram bin contents. 
New methods to create and access errror values on the bins: 
                
                  double 
                    [] getBinErrors()double 
                    getBinError(int bin)void makeBinErrors()boolean 
                    packErrors(double [] errors)  Thus the class gives us the option of the statistics from the bin 
              distribution or from the moments calculated during data entry. We 
              also added the new capability of errors on the bins. This will be 
              convenient later for data fitting. HistogramAdaptR1 
              combines HistogramAdapt 
              and HistogramMedian. 
              The key feature as before comes from the option of saving all of 
              the original data values in an array. The new class includes many 
              of the same methods as before. Methods of particular interest include: 
              double [] getDataStats() 
                - same statistics as before except that now median is now calculated 
                from the data array.
getMedian() 
                - calculates the median from the data array.
void setAdaptEnabled(boolean 
                flag) - can turn on or off the saving of data into an array. 
                If turned off, the getStats() 
                and getMedian() 
                methods revert to the HistogramStatR1 
                overriden methods.  In the example below we use these two classes to create a similar 
              applet as that in the Median section. 
              
              
                 
                 
                  |  
                       HistR1Applet.java 
                        - displays an instance of HistogramAdaptR1 on a HistPanel. 
                        Generates data the same as in AdaptHistFillApplet 
                        for the Adaptable Histogram section.
 + New 
                        classes: HistogramAdaptR1.java - This subclass of HistogramStatR1 
                        provides histograms that offer the option to adapt the 
                        range to keep all points within the bin limits plus statistical 
                        information on the distribution. The class uses a data 
                        array to hold each individual entry. This class derives 
                        from a combination of HistogramAdapt and HistogramMedian.
  HistogramStatR1.java 
                        - This class derives from a refactoring of Histogram, 
                        HistogramStats, HistogramAdapt, and HistogramMedian. It 
                        provides all of the methods and properties of those classes.
 + Previous 
                        classes: Chapter 8:Tech: 
                         
                        MakeData.java, Updateable.java
 Chapter 
                        7:Tech: HistogramStat.java,
 Chapter 
                        6:Tech: Histogram.java, 
                        HistPanel.java
 Chapter 
                        6:Tech: PlotPanel.java, 
                        PlotFormat.java
 
 |  
                  | /** *  This class provides provides histograms 
                    that offer the option
 *  to adapt the range to keep all points 
                    within bin limits. This
 *  requires a data array to hold each 
                    individual entry.
 *  Requires an expandable data storage 
                    array to hold each data point.
 *
 *  This class derives from a combination 
                    of HistogramAdapt and
 *  HistogramMedian.
 **/
 public class HistogramAdaptR1 extends HistogramStatR1
 {
 // flag for adapting the limits to the data
 boolean fAdapt = true;
 
 // flag to indicate a value outside limits
 boolean fNeedToRebin = false;
 
 // data array.
 double [] fData ;
 
 // index pointing where next datum goes
 int fDataIndex = 0;
 
 /**
 * Constructor
 *
 * @param estmated number of data points.
 *   Negative to turn off 
                    adaptable binning.
 * @param numbins - number of bins 
                    for the histogram.
 * @param lo - lowest value of bin.
 * @param hi - highest value of bin.
 * @param numData - size of data array. 
                    Must be equal to or greater
 *        than 
                    the number of data points expected to be entered
 *        into 
                    the histogram.
 **/
 public HistogramAdaptR1 (int num_bins, double 
                    lo, double hi,
 int 
                    num_data) {
 super (num_bins,lo,hi);
 if (num_data <= 0) fAdapt = false;
 else setData (num_data);
 } // ctor
 
 /**
 * Constructor with title and x axis 
                    label.
 *
 * @param title for histogram
 * @param estmated number of data points.
 *   Negative to turn off 
                    adaptable binning.
 * @param label for x axis of histogram.
 * @param inital number of bins for 
                    the histogram.
 * @param lowest value of bin.
 * @param highest value of bin.
 * @param size of data array. Must 
                    be equal to or greater
 *        than 
                    the number of data points expected to be entered
 *        into 
                    the histogram.
 **/
 public HistogramAdaptR1 (String title, String 
                    xLabel, int num_bins,
 double lo, double hi, int num_data) {
 super (title, xLabel, num_bins,lo,hi);
 if (num_data <= 0 ) fAdapt = false;
 else setData (num_data);
 } // ctor
 
 /**
 *  Create a data array sufficient 
                    to include the data.
 *  @param number of data 
                    elements needed. If data already
 *  present in the current 
                    array, then the array will be big
 *  enough to hold the current 
                    data plus numData.
 **/
 void setData (int num_data) {
 // If previous data already present 
                    then
 // check if room for the new data. 
                    If not
 // then create a bigger data array.
 if (fDataIndex > 0) {
 int data_needed 
                    = fDataIndex + num_data + 1;
 if (data_needed 
                    > fData.length) {
 // 
                    Make a new data array
 double 
                    [] tmp = new double[data_needed];
 // 
                    Copy the old data into it
 System.arraycopy 
                    (fData,0,tmp,0,fData.length);
 // 
                    Move reference to new array
 fData 
                    = tmp;
 }
 }
 else // Create an initial or bigger 
                    data array if needed.
 if (fData == null || num_data > fData.length)
 fData = new 
                    double[num_data];
 } // setData
 
 /**
 * Clear the histogram and also reset 
                    the data array index
 * to the beginning.
 **/
 public void reset () {
 super.clear (); // Clear histogram 
                    arrys
 fDataIndex = 0; // reset data array 
                    pointer to beginning
 }
 
 /**
 *  Provide access to the 
                    data array. Returns
 *  null if the range adaptation 
                    turned off.
 **/
 double [] getData () {
 return fData;
 }
 
 /**
 * Can enable or disable the range adaption.
 *
 * @param boolean to turn on or off the extending 
                    of the limits
 *   when data values outside current 
                    limits occur.
 **/
 void setAdaptEnabled (boolean flag) {
 fAdapt = flag;
 if (!fAdapt) fData = null;
 }
 
 /**  Return index to data array element 
                    pointer. **/
 int getDataIndex () {
 return fDataIndex;
 }
 
 /**  Find if rebin request turned on. 
                    **/
 boolean getRebinFlag () {
 return fNeedToRebin;
 }
 
 
 /**
 * Add an entry to the histogram. Check if value 
                    outside current
 * limits of the histogram. If it is and the adaptation 
                    flag turned
 * on, then set rebin flag.
 * Synchronize so that this method and rebin () 
                    method do not interfere.
 *
 * @param non-zero length array of int values.
 **/
 public synchronized void add (double x) {
 if (fAdapt) {
 // Add new 
                    data point to array
 if (fDataIndex 
                    < fData.length) {
 fData[fDataIndex++] 
                    = x;
 // 
                    If data point outside range, set rebin flag.
 if 
                    ( x < fLo || x > fHi) fNeedToRebin = true;
 } else
 // Could throw 
                    an exception, open a warning
 // dialog, 
                    or use setData () to create a bigger data array.
 // However, 
                    we just do a simple console print.
 System.out.println 
                    ("Data overflow");
 }
 super.add (x);
 } // add
 
 
 /**
 *  Rebin the histogram using 
                    the data array.
 *  Synchronize to avoid 
                    interference with new data
 *  entering during this 
                    method.
 **/
 public synchronized void rebin () {
 if (fDataIndex <= 1) return;
 
 // Find new limits from the out of 
                    range datum,
 for (int i=0; i < fDataIndex; i++) 
                    {
 if (fData[i] 
                    < fLo) fLo = fData[i];
 if (fData[i] 
                    > fHi) fHi = fData[i];
 }
 
 // Set new limits
 fLo = Math.floor (fLo);
 fHi = Math.ceil (fHi);
 fRange = fHi - fLo;
 
 // Clear the histogram entries
 clear ();
 
 // Refill the histogram according 
                    to the new range.
 for (int i=0; i < fDataIndex; i++) 
                    {
 super.add 
                    (fData[i]);
 }
 
 fNeedToRebin = false;
 } // rebin
 
 
 /**
 *  If adaptation turned 
                    on, get the statistical measures of
 *  the distribution directly 
                    from the data.  (Uses a two pass
 *  approach reduced numerical 
                    errors compared to using the
 *  moments.)
 *  Otherwise, use the overriden 
                    method in HistogramR1.
 *  @return  values 
                    in double array correspond to
 *    1 - mean
 *    2 - std. 
                    dev
 *    3 - error 
                    on the mean
 *    4 - skewness
 *    5 - kurtosis
 *    6 - median
 *
 *  The median is calculated 
                    from the data.
 *
 **/
 public double [] getStats () {
 if (!fAdapt) {
 return super.getStats 
                    ();
 }
 
 if (fDataIndex == 0) {
 fStats[I_MEAN]       
                    =  0.0;
 fStats[I_STD_DEV]    =  0.0;
 fStats[I_MEAN_ERROR] 
                    =  0.0;
 fStats[I_SKEWNESS]   
                    =  0.0;
 fStats[I_KURTOSIS]   
                    =  0.0;
 fStats[I_MEDIAN]     
                    =  0.0;
 return fStats;
 }
 
 double sum = 0;
 
 // First find the mean.
 for (int i=0; i < fDataIndex; i++) 
                    {
 sum += fData[i];
 }
 
 fStats[I_MEAN] = sum/fDataIndex;
 
 // Now calculate the moments.
 
 double sumDev2 = 0.0;
 double sumSkew = 0.0;
 double sumKurt = 0.0;
 
 // Now make a second pass through 
                    the data.
 for (int i=0; i < fDataIndex; i++) 
                    {
 double dev 
                    = fData[i] - fStats[I_MEAN];
 double dev2 
                    = dev * dev;
 
 sumDev2 += 
                    dev2;
 sumSkew += 
                    dev*dev2;
 sumKurt += 
                    dev2*dev2;
 }
 
 double variance = sumDev2;
 if (fDataIndex > 1)  variance 
                    /= (fDataIndex-1);
 
 fStats[I_STD_DEV] = Math.sqrt (variance);
 
 // Error on mean = s / sqrt (n)
 fStats[I_MEAN_ERROR] = fStats[I_STD_DEV]/Math.sqrt 
                    (fDataIndex);
 
 if (variance != 0) {
 double sigmaCube 
                    = fStats[I_STD_DEV] * variance;
 
 fStats[I_SKEWNESS] 
                    = sumSkew/ (fDataIndex*sigmaCube);
 fStats[I_KURTOSIS] 
                    = sumKurt/ (fDataIndex*variance*variance) - 3.0;
 } else {
 fStats[I_SKEWNESS]   
                    =  0.0;
 fStats[I_KURTOSIS]   
                    =  0.0;
 }
 fStats[I_MEDIAN] = getMedian ();
 return fStats;
 
 } // getStats
 
 /**
 *  This method overrrides 
                    the  method in HistogramR1 so as to
 *  calculate the median 
                    from the data array. If adaptation
 *  turned off, the overriden 
                    method used.
 **/
 public double getMedian () {
 
 if (fAdapt) { // then use data to 
                    get median
 double median 
                    = 0.0;
 
 if (fDataIndex 
                    > 1) {
 java.util.Arrays.sort (fData, 0, fDataIndex);
 int mid = fDataIndex/2;
 // fDataIndex points to element above last entry.
 if ((fDataIndex % 2) == 0) {
 // 
                    even number of entries
 return  (fData[mid] 
                    + fData[mid-1])/2.0;
 } else  {// odd number of entries
 return 
                    fData[mid];
 }
 } else
 return fData[0];
 } else // Otherwise, use the bin contents.
 return super.getMedian 
                    ();
 
 } // getMedian
 
 } // class HistogramAdaptR1
 |   
                  | /** * This class comes from a refactoring of Histogram,
 * HistogramStats, HistogramAdapt, and HistogramMedian.
 *
 **/
 public class HistogramStatR1 extends Histogram
 {
 protected double [] fBinErrors;
 protected boolean fDoDataStats = true;
 
 // These constants indicate for each element of 
                    the
 // array returned from the getStats () method 
                    the statistical
 // measure to which it corresponds.
 public final static int I_MEAN       
                    = 0;
 public final static int I_STD_DEV    = 
                    1;
 public final static int I_MEAN_ERROR = 2;
 public final static int I_SKEWNESS   
                    = 3;
 public final static int I_KURTOSIS   
                    = 4;
 public final static int I_MEDIAN     
                    = 5;
 public final static int I_NUMSTATS   
                    = 6;
 
 protected double [] fStats = new double[I_NUMSTATS];
 
 protected double [] fMoments= new double[5];
 
 //--- Constructors -----------------------------------------
 /**
 * The constructor will create an array of a given
 * number of bins. The range of the histogram 
                    given
 * by the upper and lower limit values.
 *
 * @param numBins number of bins for the histogram.
 * @param lo lowest value of bin.
 * @param hi highest value of bin.
 **/
 public HistogramStatR1 (int num_bins, double lo, 
                    double hi) {
 super (num_bins,lo,hi);
 } // ctor
 
 /**
 * Constructor with title and x axis label.
 *
 * @param title histogram title
 * @param xLabel label for x axis of histogram.
 * @param numBins number of bins for the histogram.
 * @param lo lowest value of bin.
 * @param hi highest value of bin.
 **/
 public HistogramStatR1 (String title, String xLabel, 
                    int num_bins,
 double lo, double hi) {
 super (title, xLabel, num_bins,lo,hi);
 } // ctor
 
 
 /** Get the number of entries in the smallest 
                    bin. **/
 public int getMin () {
 int min = getMax ();
 
 for (int i=0; i < fNumBins;i++)
 if (min > 
                    fBins[i]) min = fBins[i];
 
 return min;
 }
 
 
 //--- Bin  modification ------------------------------------
 
 /**
 * Add an entry to a bin. Include if 
                    value is in the range
 *    lo <= x < 
                    hi
 **/
 public void add (double x) {
 if (x >= fHi)
 fOverflows++;
 else  if ( x < fLo)
 fUnderflows++;
 else  {
 double val 
                    = x - fLo;
 
 // Casting 
                    to int will round off to lower
 // integer 
                    value.
 int bin =  (int) 
                    (fNumBins *  (val/fRange) );
 
 // Increment 
                    the corresponding bin.
 fBins[bin]++;
 }
 
 // Calculate raw moments from each 
                    data value.
 fMoments[0] += 1;
 fMoments[1] += x;
 double x2 = x * x;
 fMoments[2] += x2;
 fMoments[3] += x2 * x;
 fMoments[4] += x2 * x2;
 }
 
 /** Clear the histogram bins and the over and 
                    under flows. **/
 public void clear () {
 // Clear the bins, over and under 
                    flosts
 super.clear ();
 
 // Clear the bin errors if they exist.
 int i;
 for (i=0; i < 5; i++) fMoments[i] 
                    = 0.0;
 if (fBinErrors != null)
 for (i=0; i < fBinErrors.length; 
                    i++) fBinErrors[i] = 0.0;
 
 }
 
 
 //--- Statistics methods -----------------------------------
 
 /**
 * Get the average and standard deviation 
                    from the
 * distribution of the bins rather 
                    than from the
 * individual data inputs as in getStats 
                    () method.
 * Overflows and underflows ignored.
 
 
 * If histogram empty, all values set 
                    to zero.
 **/
 public double [] getStats () {
 
 int total = getTotal ();
 
 if (total == 0) {
 fStats[I_MEAN]       
                    =  0.0;
 fStats[I_STD_DEV]    =  0.0;
 fStats[I_MEAN_ERROR] 
                    =  0.0;
 fStats[I_SKEWNESS]   
                    =  0.0;
 fStats[I_KURTOSIS]   
                    =  0.0;
 fStats[I_MEDIAN]     
                    =  0.0;
 return fStats;
 }
 
 double wt_total = 0;
 double bin_width = fRange/fNumBins;
 
 double bin_mid = 0.5 * bin_width + 
                    fLo;
 
 // First get the average using the
 // middle of the bin as the position
 // and weighted by the bin contents.
 for (int i=0; i < fNumBins;i++) {
 total += fBins[i];
 bin_mid += 
                    bin_width;
 wt_total  += 
                    fBins[i] * bin_mid;
 }
 
 fStats[I_MEAN] = wt_total/total;
 
 // Now calculate the moments.
 bin_mid = 0.5 * bin_width + fLo;
 
 double sum_dev2 = 0.0;
 double sum_skew = 0.0;
 double sum_kurt = 0.0;
 
 for (int i=0; i < fNumBins; i++) {
 double dev 
                    = bin_mid - fStats[I_MEAN];
 double dev2 
                    = dev * dev;
 
 sum_dev2 += 
                    dev2;
 sum_skew += 
                    dev  * dev2;
 sum_kurt += 
                    dev2 * dev2;
 }
 
 double variance = sum_dev2;
 if (total > 1) variance /= (total-1);
 
 fStats[I_STD_DEV] = Math.sqrt (variance);
 // Error on mean = s / sqrt (n)
 fStats[I_MEAN_ERROR] = fStats[I_STD_DEV]/Math.sqrt 
                    (total);
 
 if (variance != 0) {
 double sigmaCube 
                    = fStats[I_STD_DEV]  * variance;
 fStats[I_SKEWNESS] 
                    = sum_skew/ (total * sigmaCube);
 fStats[I_KURTOSIS] 
                    = sum_kurt/ (total * variance*variance) - 3.0;
 } else {
 fStats[I_SKEWNESS]   
                    =  0.0;
 fStats[I_KURTOSIS]   
                    =  0.0;
 }
 
 fStats[I_MEDIAN] = getMedian ();
 
 return fStats;
 
 } // getStats
 
 /**
 *  Get the statistical measures 
                    of the distribution calculated
 *  from the individual entry 
                    values  (except the median).
 *  @return  values 
                    in double array correspond to
 *    1 - mean
 *    2 - std. 
                    dev
 *    3 - error 
                    on the mean
 *    4 - skewness
 *    5 - kurtosis
 *    6 - median
 *
 *  The median is calculated 
                    from the bin distribution.
 *
 **/
 public double [] getDataStats () {
 
 double n = fMoments[0];
 
 // Average value = 1/n * sum[x]
 double mean = fMoments[1]/n;
 
 // Use running mean.
 fStats[I_MEAN] = mean;
 double mean_sq = mean*mean;
 
 // Check on minimum number of entries.
 if ( n < 2) return fStats;
 
 // Convert power sums to central moments
 double m2 = fMoments[2]/n;
 double cm2 = m2 - mean * mean;
 double m3 = fMoments[3]/n;
 double cm3 = 2.0 * mean * mean_sq 
                    - 3.0 * mean * m2 + m3;
 double m4 = fMoments[4]/n;
 double cm4 = -3.0 * mean_sq * mean_sq 
                    + 6.0 * mean_sq * m2
 -4.0 * mean * m3 + m4;
 
 // variance = N/ (N-1) m2
 double variance = cm2 * (n/ (n-1.0));
 
 // Std. Deviation s = sqrt (variance)
 fStats[I_STD_DEV] = Math.sqrt (variance);
 
 // Error on mean = s / sqrt (N)
 fStats[I_MEAN_ERROR] = fStats[1]/Math.sqrt 
                    (n);
 
 // Skewness = n^2/ (n-1) (n-2) *  cm3/s^3
 fStats[I_SKEWNESS] =  ( 
                    n/ ( (n-1) * (n-2)) ) * n * cm3 /
 (variance*fStats[I_STD_DEV]);
 
 // Kurtosis = n (n+1)/ (n-1) (n-2) 
                    (n-3) * cm4/s^4 - 3 (n-1)^2 / (n-2) (n-3)
 double factor1 =  ( n * 
                    (n+1.0))/ (  (n-1.0) * (n-2.0) * (n-3.0) );
 double factor2 =  ( 3.0 
                    * (n-1.0) * (n-1.0) )/ ( (n-2.0) * (n-3.0));
 fStats[I_KURTOSIS] = factor1 * cm4 
                    * n/ (variance * variance) - factor2;
 
 fStats[I_MEDIAN]  = getMedian 
                    ();
 
 return fStats;
 
 } // getDataStats
 
 /**
 *  The median is calculated 
                    from the bin distribution.
 *  Overflows and underflows 
                    ignored.
 **/
 public double getMedian () {
 
 int half_total_entries = getTotal 
                    ()/2;
 int sum_bin_entries = 0;
 int sum = 0;
 
 double bin_width = fRange/fNumBins;
 double median = 0.0;
 
 // Sum bins up to half total of entries
 for ( int i=0; i < fNumBins; i++) 
                    {
 sum = sum_bin_entries 
                    + fBins[i];
 
 // Check if bin crosses 
                    halfTotal point
 if (sum >= half_total_entries 
                    ) {
 // 
                    Scale linearly across the bin
 int 
                    dif = half_total_entries - sum_bin_entries;
 double 
                    frac = 0.0;
 if 
                    (fBins[i] > 0) {
 frac 
                    =  ((double)dif)/(double)fBins[i];
 }
 median 
                    = (i + frac) * bin_width + fLo;
 // 
                    Finished
 break;
 }
 // Not reached median 
                    yet.
 sum_bin_entries = sum;
 }
 return median;
 } // getMedian
 
 //--- Bin error methods -----------------------------------
 
 /**
 * Provide an array of error values 
                    for all bins.
 * @return array of double values.
 **/
 public double [] getBinErrors () {
 return fBinErrors;
 }
 
 /**
 * Return the error for a particular 
                    bin.
 * @param from 0 to highest bin value
 **/
 public double getBinError (int bin) {
 if (bin>= 0 && bin < fBins.length)
 return fBinErrors[bin];
 else
 return -1.0;
 } // getBinError
 
 /**
 * Calculate the error on each bin 
                    according to the
 * sqrt (bin contents) from std. dev. 
                    of Poisson distribution.
 **/
 public void makeBinErrors () {
 if (fBinErrors == null ||
 fBinErrors.length 
                    != fBins.length)
 fBinErrors 
                    = new double [fBins.length];
 
 for ( int i = 0; i < fNumBins; i++)
 {
 fBinErrors[i] = Math.sqrt 
                    (fBins[i]);
 }
 } // makeBinErrors
 
 /**
 * Pack the errors on each bin.
 * @param false if array size doesn't
 *        match 
                    number of bins.
 */
 public boolean packErrors (double [] errors) {
 if (errors.length != fNumBins) return 
                    false;
 if (fBinErrors == null || fBinErrors.length 
                    != fNumBins)  {
 fBinErrors 
                    = new double[errors.length];
 }
 
 for (int i = 0; i < fNumBins; i++)  {
 fBinErrors[i] 
                    = errors[i];
 }
 return true;
 } //packErrors
 
 } // class HistogramStatR1
 |   
                  | import 
                    javax.swing.*; import java.awt.*;
 import java.awt.event.*;
 
 /**
 * The applet uses the HistPanel to display contents of
 * an instance of HistogramAdaptR1.
 *
 * The java.util.Timer and java.util.TimerTask are used
 * to update the display of the histogram during the filling
 * of the histogram.
 *
 * Includes "Go" button to initiate the filling of the histogram.
 * To simulate data taking, a combination of a Gaussian and 
                    random
 * background values are generated.
 *
 * The number of values taken from
 * entry in a JTextField. "Clear"  button clears 
                    the histogram.
 * In standalone mode, the Exit button closes the program.
 *
 */
 public class HistR1Applet extends JApplet
 implements ActionListener, Updateable
 {
 // Use the HistPanel JPanel subclass here
 PlotPanel fOutputPanel = null;
 
 HistogramAdaptR1 fHistogram = null;
 int fNumDataPoints = 100;
 
 boolean fMakingHist = false;
 boolean fUpdateDisplay = false;
 MakeData fMakeData;
 
 // Use the java.util Timer and TimerTask combo
 // for timing events.
 java.util.Timer fTimer;
 
 // A text field for input strings
 JTextField fTextField = null;
 
 // Flag for whether the applet is in a browser
 // or running via the main () below.
 boolean fInBrowser=true;
 
 //Buttons
 JButton fGoButton;
 JButton fStatsButton;
 JButton fClearButton;
 JButton fExitButton;
 
 /**
 * Create a User Interface with a histogram 
                    and a Go button
 * to initiate processing and a Clear 
                    button to clear the .
 * histogram. In application mode, 
                    the Exit button stops the
 * program. Add a stats button to open 
                    a frame window to show
 * statistical measures.
 **/
 public void init () {
 
 Container content_pane = getContentPane 
                    ();
 
 JPanel panel = new JPanel (new BorderLayout 
                    ());
 
 // Use a textfield for an input parameter.
 fTextField =
 new JTextField (Integer.toString 
                    (fNumDataPoints), 10);
 
 // If return hit after entering text, 
                    the
 // actionPerformed will be invoked.
 fTextField.addActionListener (this);
 
 fGoButton = new JButton ("Go");
 fGoButton.addActionListener (this);
 
 fStatsButton = new JButton ("Stats");
 fStatsButton.addActionListener (this);
 
 fClearButton = new JButton ("Clear");
 fClearButton.addActionListener (this);
 
 fExitButton = new JButton ("Exit");
 fExitButton.addActionListener (this);
 
 JPanel control_panel = new JPanel 
                    ();
 
 control_panel.add (fTextField);
 control_panel.add (fGoButton);
 control_panel.add (fStatsButton);
 control_panel.add (fClearButton);
 control_panel.add (fExitButton);
 
 // Create a histogram and start filling 
                    it.
 makeHist ();
 fGoButton.setText ("Stop");
 fClearButton.setEnabled (false);
 fStatsButton.setEnabled (false);
 if (fInBrowser) fExitButton.setEnabled 
                    (false);
 
 // JPanel subclass here.
 fOutputPanel = new HistPanel (fHistogram);
 
 panel.add (fOutputPanel,"Center");
 panel.add (control_panel,"South");
 
 // Add text area with scrolling to 
                    the contentPane.
 content_pane.add (panel);
 } // init
 
 public void actionPerformed (ActionEvent e) {
 Object source = e.getSource ();
 if (source == fGoButton || source 
                    == fTextField) {
 String strNumDataPoints 
                    = fTextField.getText ();
 try {
 fNumDataPoints 
                    = Integer.parseInt (strNumDataPoints);
 }
 catch (NumberFormatException 
                    ex) {
 // 
                    Could open an error dialog here but just
 // 
                    display a message on the browser status line.
 showStatus 
                    ("Bad input value");
 return;
 }
 if (!fMakingHist) 
                    {
 makeHist ();
 fGoButton.setText ("Stop");
 fStatsButton.setEnabled (false);
 fClearButton.setEnabled (false);
 } else {
 // Stop button has been pushed
 fGoButton.setText ("Go");
 fStatsButton.setEnabled (true);
 fClearButton.setEnabled (true);
 fMakingHist = false;
 }
 } else if (source == fStatsButton) 
                    {
 displayStats 
                    ();
 } else if ( source == fClearButton) 
                    {
 // Use reset 
                    instead of clear so as to
 // set data 
                    array pointer to zero.
 fHistogram.reset 
                    ();
 repaint ();
 } else if (!fInBrowser)
 System.exit 
                    (0);
 
 } // actionPerformed.
 
 /** Create a frame to display the distribution 
                    statistics. **/
 void displayStats () {
 JFrame frame =
 new JFrame 
                    ("Histogram Distributions Statistics");
 
 // Create a listener to close the 
                    frame
 WindowListener listener =
 new WindowAdapter ()
 {
 public void 
                    windowClosing (WindowEvent e)
 {
 JFrame 
                    f =  (JFrame) (e.getSource ());
 f.dispose 
                    ();
 }
 };
 frame.addWindowListener (listener);
 
 JTextArea area = new JTextArea ();
 
 double [] stats = fHistogram.getStats 
                    ();
 if (stats != null)  {
 area.append ("Number entries 
                    = "+fHistogram.getTotal ()+"\n");
 
 String stat = PlotFormat.getFormatted 
                    (
 stats[HistogramStatR1.I_MEDIAN],
 1000.0,0.001,3);
 area.append ("Median value 
                    = "+ stat +" "+"\n");
 
 stat = PlotFormat.getFormatted 
                    (
 stats[HistogramStatR1.I_MEAN],
 1000.0,0.001,3);
 area.append ("Mean value 
                    = "+ stat +" ");
 
 stat = PlotFormat.getFormatted 
                    (
 stats[HistogramStatR1.I_MEAN_ERROR],
 1000.0,0.001,3);
 area.append (" +/- "+stat+"\n");
 
 stat = PlotFormat.getFormatted 
                    (
 stats[HistogramStatR1.I_STD_DEV],
 1000.0,0.001,3);
 area.append ("Std. Dev. 
                    = "+stat+"\n");
 
 stat = PlotFormat.getFormatted 
                    (
 stats[HistogramStatR1.I_SKEWNESS],
 1000.0,0.001,3);
 area.append ("Skewness 
                    = "+stat+"\n");
 
 stat = PlotFormat.getFormatted 
                    (
 stats[HistogramStatR1.I_KURTOSIS],
 1000.0,0.001,3);
 area.append ("Kurtosis 
                    = "+stat+"\n");
 } else {
 area.setText ("No statistical 
                    information available");
 }
 
 frame.getContentPane ().add (area);
 frame.setSize (200,200);
 frame.setVisible (true);
 
 } // displayStats
 
 /**
 *  Create the histogram, 
                    create the MakeData instance and
 *  spin it off in a thread. 
                    Set up a java.util.Timer to
 *  run the PaintHistTask 
                    periodically.
 **/
 void makeHist () {
 if ( fMakingHist) return; // only 
                    fill one hist at a time.
 fMakingHist 
                    = true;
 
 // Create an instance of the histogram 
                    class.
 // Make it wide enough enough to include 
                    the data.
 if (fHistogram == null)
 fHistogram 
                    = new HistogramAdaptR1 ("Gaussian + Random Background",
 "Data",
 20,-2.0,2.0,
 fNumDataPoints);
 else
 fHistogram.setData 
                    (fNumDataPoints);
 
 // Create the runnable object to fill 
                    the histogram
 // Center signal at 3.0 and create 
                    background between
 // -10 and 10. The fraction of the 
                    data due to the
 // Gaussian will be 0.60. The maximum 
                    delay between
 // data poins will be 500msecs.
 fMakeData =
 new MakeData (this, fHistogram, 
                    fNumDataPoints,
 3.0, 0.60, -10.0, 10.0, 500);
 Thread dataThread = new Thread (fMakeData);
 
 // Before starting the filling, create 
                    the timer task
 // that will cause the histogram display 
                    to update
 // during the filling.
 // Create a timer. TimerTask created 
                    in MakeHist ()
 fTimer = new java.util.Timer ();
 
 // Start the timer after 100ms and 
                    then repeat calls
 // to run in PaintHistTask object 
                    every 250ms.
 fTimer.schedule (new PaintHistTask 
                    (), 100, 250);
 
 // Now start the data filling.
 dataThread.start ();
 
 } // makeHist
 
 /**
 * Use the inner class technique to 
                    define the
 * TimerTask subclass for signalling 
                    that the display
 * should be updated.
 **/
 class PaintHistTask extends java.util.TimerTask
 {
 public void run () {
 fUpdateDisplay = true;
 }
 } // class PaintHistTask
 
 /**
 *  Repaint the histogram 
                    display. If called by
 *  the MakeData object, 
                    it will turn off the
 *  update display flag and 
                    return the fMakingHist flag
 *  to indicate if updating 
                    should continue.
 **/
 public boolean update (Object obj) {
 
 // Don't update the display until 
                    the timer
 // turns on the fUpdateDisplay flag.
 if ( fUpdateDisplay)  {
 // Possible this method 
                    called before fOutputPanel
 // created in the init 
                    (). So check if it exists
 // before attempting to 
                    repaint.
 if (fOutputPanel != null) 
                    {
 // Check if 
                    binning needs changing before
 // redrawing 
                    histogram.
 if ( fHistogram.getRebinFlag 
                    () ) fHistogram.rebin ();
 
 // Now rescale 
                    and draw.
 fOutputPanel.getScaling 
                    ();
 fOutputPanel.repaint 
                    ();
 }
 fUpdateDisplay = false;
 }
 return fMakingHist;
 } // Update
 
 /**  Called when the histogram filling 
                    is finished. **/
 public void done () {
 fMakingHist = false;
 
 // Stop the histogram display updates.
 fTimer.cancel ();
 
 // Do a final check if binning needs 
                    changing and to
 // redraw histogram.
 if (fHistogram.getRebinFlag () ) fHistogram.rebin 
                    ();
 fOutputPanel.getScaling ();
 fOutputPanel.repaint ();
 
 // Reset the buttons.
 fGoButton.setText ("Go");
 
 fGoButton.setEnabled (true);
 fStatsButton.setEnabled (true);
 fClearButton.setEnabled (true);
 
 } // done
 
 public static void main (String[] args) {
 //
 int frame_width=450;
 int frame_height=300;
 
 //
 HistR1Applet applet = new HistR1Applet 
                    ();
 applet.fInBrowser = false;
 applet.init ();
 
 // Following anonymous class used 
                    to close window & exit program
 JFrame f = new JFrame ("Demo");
 f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
 
 // Add applet to the frame
 f.getContentPane ().add ( applet);
 f.setSize (new Dimension (frame_width,frame_height));
 f.setVisible (true);
 } // main
 
 } // class HistR1Applet
 |    References 
              & Web Resources Last update: Nov. 8, 2004 |