> ### Solution for
> Programming Exercise 5.2

> THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

> Exercise 5.2: A common programming task is computing statistics of a set of numbers. (A statistic is a number that summarizes some property of a set of data.) Common statistics include the mean (also known as the average) and the standard deviation (which tells how spread out the data are from the mean). I have written a little class called `StatCalc` that can be used to compute these statistics, as well as the sum of the items in the dataset and the number of items in the dataset. You can read the source code for this class in the file StatCalc.java. If `calc` is a variable of type `StatCalc`, then the following methods are defined:

> - `calc.enter(item);` where `item` is a number, adds the item to the dataset.
> - `calc.getCount()` is a function that returns the number of items that have been added to the dataset.
> - `calc.getSum()` is a function that returns the sum of all the items that have been added to the dataset.
> - `calc.getMean()` is a function that returns the average of all the items.
> - `calc.getStandardDeviation()` is a function that returns the standard deviation of the items.

> Typically, all the data are added one after the other calling the `enter()` method over and over, as the data become available. After all the data have been entered, any of the other methods can be called to get statistical information about the data. The methods `getMean()` and `getStandardDeviation()` should only be called if the number of items is greater than zero.

> Modify the current source code, `StatCalc.java`, to add instance methods `getMax()` and `getMin()`. The `getMax()` method should return the largest of all the items that have been added to the dataset, and `getMin()` should return the smallest. You will need to add two new instance variables to keep track of the largest and smallest items that have been seen so far.

> Test your new class by using it in a program to compute statistics for a set of non-zero numbers entered by the user. Start by creating an object of type `StatCalc`:

>                  StatCalc  calc;   // Object to be used to process the data.
>                  calc = new StatCalc();

> Read numbers from the user and add them to the dataset. Use 0 as a sentinel value (that is, stop reading numbers when the user enters 0). After all the user's non-zero numbers have been entered, print out each of the six statistics that available from `calc`.

> Discussion

> For the `StatCalc` class to handle minimums and maximums, some of what must be added to the class is obvious. We needs two new instance variables, `min` and `max`, and two methods to return the values of those instance variables. So, we can add these lines to the class definition:

>             private double min;   // Smallest item that has been entered.
>             private double max;   // Largest item seen that has been entered.

>             public double getMin() {
>                  // Return the smallest item that has been entered.
>                return min;
>             }

>             public double getMax() {
>                  // Return the largest item that has been entered.
>                return max;
>             }

> But then there is the problem of making sure that `min` and `max` have the right values. Every time we have a new number to add to the dataset, we have to compare it with `min`. If the new number is smaller than the current `min`, then the number becomes the new value of `min` (since the new number is now the smallest number we have seen so far). We do something similar for `max`. This has to be done whenever a number is entered into the dataset, so it has to be added to the `enter()` method, giving:

>             public void enter(double num) {
>                   // Add the number to the dataset.
>                count++;
>                sum += num;
>                squareSum += num*num;
>                if (num > max)          // We have a new maximum.
>                   max = num;
>                if (num < min)          // We have a new minimum.
>                   min = num;
>             }

> Unfortunately, if this is all we do, there is a bug in our program. For example, if the dataset consists of the numbers 21, 17, and 4, the computer will insist that the minimum is 0, rather than 4. The problem is that the variables `min` and `max` are initialized to zero. (If no initial value is provided for a numerical instance variable, it gets the default initial value, zero.) Since `min` is 0, none the numbers in the dataset pass the test "`if (num < min)`", so the value of `min` never changes. A similar problem holds for `max`, but it will only show up if all the numbers in the dataset are less than zero. For the other instance variables, `count`, `sum`, and `squareSum`, the default initial value of zero is correct. For `min` and `max`, we have to do something different.

> Once possible way to fix the problem is to treat the first number entered as a special case. When only one number has been entered, it's certainly the largest number so far and also the smallest number so far, so it should be assigned to both `min` and `max`. This can be handled in the `enter()` method:

>             public void enter(double num) {
>                   // Add the number to the dataset.
>                   // (This is NOT the version I used in my final answer.)
>                count++;
>                sum += num;
>                squareSum += num*num;
>                if (count == 1) {  // This is the fist number.
>                   max = num;
>                   min = num;
>                }
>                else {
>                   if (num > max)     // We have a new maximum.
>                      max = num;
>                   if (num < min)     // We have a new minimum.
>                      min = num;
>                }
>             }

> This works fine. However, I decided to use an alternative approach. We would be OK if we could initialize `min` to have a value that is bigger than any possible number. Then, when the first number is entered, it will have to satisfy the test "`if (num < min)`", and it will become the value of `min`. But to be bigger than any possible number, `min` would have to be infinity. The initial value for `max` has to be smaller than any possible number, so `max` has to be initialized to negative infinity. And that's what we'll do!

> The system that is used to represent real numbers in a computer includes special values to represent infinity and negative infinity. Java has a standard class named `Double` that includes named constants for these quantities, `Double.POSITIVE_INFINITY` and `Double.NEGATIVE_INFINITY`. We can use these named constants to provide initial values for the instance variables `min` and `max`. So, the declarations become:

>             private double max = Double.NEGATIVE_INFINITY;  // Largest item seen.
>             private double min = Double.POSITIVE_INFINITY;  // Smallest item seen.

> With this change, the `StatCalc` class works correctly. The complete class is shown below. (By the way, there is another special constant, `Double.NaN`, that represents an undefined value such as the result of dividing a number by 0.0. "NaN" stands for "Not a Number.")

> The main program is fairly straightforward. The user's data are read and entered into the `StatCalc` object in a loop:

>                do {
>                   TextIO.put("? ");
>                   item = TextIO.getlnDouble();
>                   if (item != 0)
>                      calc.enter(item);
>                } while ( item != 0 );

> The subroutine call "`calc.enter(item);`" enters the user's item. That is, it does all the processing necessary to include this data item in the statistics it is computing. After all the data have been entered, the statistics can be obtained by using function calls such as "`calc.getMean()`". The statistics are output in statements such as:

>                TextIO.putln("   Average:            " + calc.getMean());

> Note that a function call represents a value, and so can be used anyplace where a variable or literal value could be used. I don't have to assign the value of the function to a variable. I can use the function call directly in the output statement.

> The complete main program is shown below.

> Although that completes the exercise, one might wonder: Instead of modifying the source code of `StatCalc`, could we make a subclass of `StatCalc` and put the modifications in that? The answer is yes, but we need to use the slightly obscure special variable `super` that was discussed in Section 5.5.

> The new instance variables and instance methods can simply be put into the subclass. The problem arises with the `enter()` method. We have to redefine this method so that it will update the values of `min` and `max`. But it also has to do all the processing that is done by the original `enter()` method in the `StatCalc` class. This is what `super` is for. It lets us call a method from the superclass of the class we are writing. So, the subclass can be written:

>          class StatCalcWithMinMax extends StatCalc {

>             private double max = Double.NEGATIVE_INFINITY;  // Largest item seen.
>             private double min = Double.POSITIVE_INFINITY;  // Smallest item seen.

>             public void enter(double num) {
>                   // Add the number to the dataset.
>                super.enter(num);  // Call the enter method from the StatCalc class.
>                if (num > max)  // Then do the extra processing for min and max.
>                   max = num;
>                if (num < min)
>                   min = num;
>             }

>             public double getMin() {
>                  // Return the smallest item that has been entered.
>                  // Value will be infinity if no items have been entered.
>                return min;
>             }

>             public double getMax() {
>                  // Return the largest item that has been entered.
>                  // Value will be -infinity if no items have been entered.
>                return max;
>             }

>          }  // end class StatCalcWithMinMax

> The Solution

>     Revised StatCalc Class

>          /*
>             An object of class StatCalc can be used to compute several simple statistics
>             for a set of numbers.  Numbers are entered into the dataset using
>             the enter(double) method.  Methods are provided to return the following
>             statistics for the set of numbers that have been entered: The number
>             of items, the sum of the items, the average, the standard deviation,
>             the maximum, and the minimum.
>          */

>          public class StatCalc {

>             private int count;   // Number of numbers that have been entered.
>             private double sum;  // The sum of all the items that have been entered.
>             private double squareSum;  // The sum of the squares of all the items.
>             private double max = Double.NEGATIVE_INFINITY;  // Largest item seen.
>             private double min = Double.POSITIVE_INFINITY;  // Smallest item seen.

>             public void enter(double num) {
>                   // Add the number to the dataset.
>                count++;
>                sum += num;
>                squareSum += num*num;
>                if (num > max)
>                   max = num;
>                if (num < min)
>                   min = num;
>             }

>             public int getCount() {
>                   // Return number of items that have been entered.
>                return count;
>             }

>             public double getSum() {
>                   // Return the sum of all the items that have been entered.
>                return sum;
>             }

>             public double getMean() {
>                   // Return average of all the items that have been entered.
>                   // Value is Double.NaN if count == 0.
>                return sum / count;
>             }

>             public double getStandardDeviation() {
>                  // Return standard deviation of all the items that have been entered.
>                  // Value will be Double.NaN if count == 0.
>                double mean = getMean();
>                return Math.sqrt( squareSum/count - mean*mean );
>             }

>             public double getMin() {
>                  // Return the smallest item that has been entered.
>                  // Value will be infinity if no items have been entered.
>                return min;
>             }

>             public double getMax() {
>                  // Return the largest item that has been entered.
>                  // Value will be -infinity if no items have been entered.
>                return max;
>             }

>          }  // end class StatCalc

>     Main Program

>          /*
>              Computes and display several statistics for a set of non-zero
>              numbers entered by the user.  (Input ends when user enters 0.)
>              This program uses StatCalc.java.0
>          */

>          public class SimpleStats {

>             public static void main(String[] args) {

>                StatCalc calc;  // Computes stats for numbers entered by user.
>                calc = new StatCalc();

>                double item;    // One number entered by the user.

>                TextIO.putln("Enter your numbers.  Enter 0 to end.");
>                TextIO.putln();

>                do {
>                   TextIO.put("? ");
>                   item = TextIO.getlnDouble();
>                   if (item != 0)
>                      calc.enter(item);
>                } while ( item != 0 );

>                TextIO.putln("\nStatistics about your calc:\n");
>                TextIO.putln("   Count:              " + calc.getCount());
>                TextIO.putln("   Sum:                " + calc.getSum());
>                TextIO.putln("   Minimum:            " + calc.getMin());
>                TextIO.putln("   Maximum:            " + calc.getMax());
>                TextIO.putln("   Average:            " + calc.getMean());
>                TextIO.putln("   Standard Deviation: " + calc.getStandardDeviation());

>             }  // end main()

>          } // end SimpleStats

> [ Exercises | Chapter Index | Main Index ]