CS 112
Fall 2020

Old version

This is the CS 112 site as it appeared on December 31, 2020.

Problem Set 3

due by 11:59 p.m. on Tuesday October 6th, 2020

General Guidelines

Preliminaries

In your work on this assignment, make sure to abide by the collaboration policies of the course.

If you have questions while working on this assignment, please come to office hours, post them on Piazza, or email instructor.

Make sure to follow the instructions outlined at the end of Part I and Part II when submitting your assignment.


Part I

52 points total

Creating the necessary file

Problems in Part I will be completed in a single PDF file. To create it, you should do the following:

  1. Open the template that we have created for these problems in Google Docs: ps3_partI

  2. Select File->Make a copy..., and save the copy to your Google Drive using the name ps3_partI.

  3. Add your work to this file.

  4. Once you have completed all of these problems, choose File->Download->PDF document, and save the PDF file on your machine. The resulting PDF file (ps3_partI.pdf) is the one that you will submit. See the submission guidelines at the end of Part I.

Problem 1: A Rectangle class revisited

12 points total; individual-only

Given the Rectangle class defined here.

  1. Consider a potential non-static method named rotate that would rotate a Rectangle object 90 degrees by swapping its width and height values. For example, if a Rectangle‘s dimensions are 10 x 30, then calling the rotate method would change its dimensions to 30 x 10. Because the method only needs to change the internals of the called object, it doesn’t need to – and should not! – return a value.

    1. (1 point) What type of instance method would rotate be, an accessor or mutator?

    2. (2 points) Give an appropriate method header for this method, making sure to take into account the fact that the method is non-static. You do not need to define the body of the method.

  2. Now consider a potential non-static method named largerThan that takes another Rectangle object and determines if the called Rectangle object (i.e., the one on which the method is invoked) has a larger area than the other Rectangle object – returning true if the called object has a larger area and false otherwise.

    1. (1 point) What type of instance method would largerThan be, an accessor or mutator?
    2. (2 points) Give an appropriate method header for this method, making sure to take into account the fact that the method is non-static. You do not need to define the body of the method.
  3. Consider the following client code — i.e., code from another class that uses a Rectangle object:

    Rectangle r1 = new Rectangle(60, 80);
    System.out.println("r1's height is: " + r1.height);
    r1.width = r1.width + 20;
    System.out.println(r1);     // print the new dimensions
    

    Because our Rectangle class employs appropriate encapsulation, this code fragment will not compile.

    1. (2 point) Explain what problems are present in the code fragment that prevent it from compiling.
    2. (4 points) Rewrite the fragment to eliminate those problems while maintaining the intended functionality of the original version. Don’t make any unnecessary changes.

Problem 2: A class that needs your help

8 points total; individual-only

Consider the following class, which is intended to serve as a blueprint for objects that encapsulate two pieces of data: an even integer and a non-negative real number:

public class ValuePair {
    int a;
    double b;

    public static double product() {
        return this.a * this.b;
    }
}
  1. (2 points) The method product is supposed to be an instance method that returns the product of the two fields inside a ValuePair object. However, when we attempt to compile this class, we get error messages that indicate that product cannot access the fields. In section 1-1 of your copy of ps3_partI (see above), explain why the method cannot access the fields, and what change or changes are needed to fix things. (Hint: What is another name for an instance method?)

  2. (6 points) This class does not employ appropriate encapsulation. In section 1-2 of ps3_partI, revise the class to prevent direct access to the internals of a ValuePair object while allowing indirect access through appropriate methods. Your revised version should include:

    • the changes that you proposed above in your answer for 1-1
    • whatever steps are needed to prevent direct access to the fields
    • accessor methods that can be used to obtain the value of each field
    • mutator methods that can be used to change the value of each field. These methods should ensure that a is always even, and that b is greater than or equal to 0.0. Attempts to assign an invalid value should produce an IllegalArgumentException.
    • a constructor that takes values for a and b and initializes the fields using those values. Attempts to assign an invalid value should produce an IllegalArgumentException. Take advantage of the error-checking code that is already present in the mutator methods that you defined for changing the values of the fields.

    No other methods are required.

Problem 3: Static vs. non-static

14 points total; individual-only

When designing a blueprint class, we have seen that we can include both static and non-static variables. Static variables (also known as class variables) belong to the class as a whole. Non-static variables (also known as instance variables or fields) belong to individual objects of the class; each object gets its own copy of those variables.

We have also seen that a blueprint class can include both static and non-static methods. A non-static method is required if the method must have access to the fields of a particular called object. However, if a method doesn’t need a called object – i.e., if all of the information that it needs is supplied by its parameters or by the static variables of the class – then we typically make it static. Non-static methods must be called on an object of the class. Static methods may be called on an object of the class, but it is better style to call them using the name of the class instead.

Imagine that we are defining a class called Grade that will serve as a blueprint for objects that encapsulate the raw score and the possible late penalty associated with a given grade. For example, to create a Grade object that represents a grade with a raw score of 85.5 and a late penalty of 20%, we would write:

Grade g = new Grade(85.5, 20);

An initial implementation of this class can be found here.

  1. (4 points) Imagine that we want to modify the existing Grade class so that each Grade object has an associated category — a String that is either "assignment", "quiz", or "exam". In addition, we want to keep track of how many Grade objects have been created for each of the three categories. What variables (static and/or non-static) would we need to add to the Grade class as part of our implementation of these changes?

    In section 2-1 of ps3_partI, we have included a table that you should complete to describe the necessary variables. For each of your proposed variables, you should specify:

    • its type and name (make it descriptive!)
    • whether it will be static or non-static
    • a brief description of its purpose, and why it needs to be static or non-static.

    As an example, we have filled in the first row of the table to describe the existing rawScore field. You should add the descriptions of your proposed new variables. You may not need all of the rows in the table.

  2. (4 points) Now let’s say that we want to add a method called setCategory that takes in a category string and uses it to change the category of the called Grade object.

    1. What type of method should setCategory be — static or non-static? Explain briefly.
    2. Assume we have a Grade object whose current category is "quiz". The professor who assigned the grade has decided to make the associated test worth more, so we need to call setCategory to change the grade’s category to "exam". During that method call, what changes will the method need to make to the values of the variables that you proposed above? Be as specific as possible.

The remaining sections of this problem consider two other new methods for the Grade class – methods that do not involve the grade’s category.

  1. (3 points) Now let’s say that we want to add a method called computePercent. It takes two parameters of type double – one called pointsEarned and another called possiblePoints – and it returns pointsEarned as a percentage of possiblePoints. For example, if pointsEarned is 30.0 and possiblePoints is 50.0, the method should return 60.0, because 30 is 60 percent of 50.

    1. What type of method should computePercent be — static or non-static? Explain briefly.
    2. Give an example of how you would call this method from outside the Grade class. If you need a Grade object to call the method, assume that the variable g represents that object, and that the object has already been created. However, you should only use g if doing so is absolutely necessary.
  2. (3 points) Finally, let’s say that we want to add a new method called addExtraCredit. It takes a parameter of type double called amount and increases the raw score of a Grade object by the specified amount.

    1. What type of method should addExtraCredit be — static or non-static? Explain briefly.
    2. Give an example of how you would call this method from outside the Grade class. If you need a Grade object to call the method, assume that the variable g represents that object, and that the object has already been created. However, you should only use g if doing so is absolutely necessary.

Problem 4: Inheritance and polymorphism

18 points total; pair-optional

This is the only problem in this assignment that you may complete with a partner. See the rules for working with a partner on pair-optional problems for details about how this type of collaboration must be structured.

Imagine that you have a set of related classes that have been defined using inheritance. Here are the key facts about these classes:

In addition, you should make the following assumptions:

Answer the following questions in light of the above information about these classes. Before you begin, you may find it helpful to draw an inheritance hierarchy for the classes, although doing so is not required.

  1. (2 points) The information above states that the Zoo class has its own equals() method that overrides the inherited one. Where does the equals() method that Zoo overrides come from? Be as specific as possible, and explain your answer briefly.

  2. (2 points) List all of the fields in a Yoo object – both the ones that it declares and the ones (if any) that it inherits.

  3. (5 points) Consider the following code fragment:

    Yoo y1 = new Yoo();
    System.out.println(y1.one(10));
    System.out.println(y1.two());
    System.out.println(y1.three(12.5));
    System.out.println(y1.equals(y1));
    System.out.println(y1);
    

    Each of the println statements in this code fragment displays the result of a method call. (This includes the fifth println statement, in which the Java interpreter calls a method on our behalf.) However, it is possible that one or more of these methods calls would fail to compile because the necessary method is neither defined in the Yoo class nor inherited from a superclass of Yoo.

    In section 3-3 of ps3_partI, we have included a table that you should complete with appropriate information about each of these method calls. We have filled in the first row for you, and you should complete the remaining rows.

  4. (3 points) Now imagine that you want to add a non-static method to the Too class. It should be called avg(), it should take no parameters, and it should return the average of all of the integer fields in the called Too object. Add a full definition of that method to section 3-4 of your ps3_partI file. Make sure to take into account the fact that the classes employ proper encapsulation. Make the average as precise as possible.

  5. (6 points) For each of the following assignment statements, indicate whether it would be allowed according to the rules of polymorphism, and explain briefly why or why not.

    1.     Woo w = new Too();

    2.     Zoo z = new Woo();

    3.     Zoo z = new Yoo();

    4.     Too t = new Zoo();

Part II Programming Problems

48 points total

Problem 5: Histogram

18 points total; pair-optional

This is the only problem in this assignment that you may complete with a partner. See the rules for working with a partner on pair-optional problems for details about how this type of collaboration must be structured.

For this problem, write a program Histogram.java which implements a static class named Histogram. This class will provide a series of static methods which will allow a client program to produce and display a histogram of a series of floating point values.

Note

A histogram is a graphical representation depicting the frequency in which numbers occur within specified ranges. Example:

[0..10], (10..20], (20..30],...., (90..100],

where [a..b] denotes the set of numbers x such that:

 a <= x <= b and

(a..b] denotes the set of numbers x such that:

 a < x <= b.

Example, assuming the following series of numbers: 1 11 11.123 41 47 51 61.7 71 81 91 2.5 12 22 44.3 42.9 52 62 72 82 92

The resulting Histogram of Values in Decades from 0 to 100 is:

[0..10]: ** 
(10..20]: *** 
(20..30]: * 
(30..40]:  
(40..50]: **** 
(50..60]: ** 
(60..70]: ** 
(70..80]: ** 
(80..90]: ** 
(90..100]: **

Begin by downloading the file: Histogram.java, which sets-up the skeleton of your class.

Important guidelines:

  • The numbers that will be used to compute the histogram should be stored in an array of doubles. The array should be large enough to store the maximum possible inputs.

  • For testing purposes, the array can be expicitly populated up to its’ maximum size, but your program must also provide a method which populates the array through user input. See below.

  • The histogram itself (i.e. counts for the various ranges) should also be stored as an array. Think what the data type of this array which represents the histogram should be.

  • Make sure to follow the method headers for each method you need to implement as specified below. Altering the method signature in any way will prevent us from testing your methods.

  1. Write a method calulateHistogram that computes the histogram from the array of numbers passed to it.

    public static int [] calculateHistogram( double [] numbers ) {
        ....
    
        // This method must determine the appropriate bin of the 
        // histogram to update by using a loop. To be clear,
        // you may NOT just explicitly account for all  
        // possibilities, e.g., the following is a bad solution:
    
        if(number <= 10.0) {           // VERY BAD SOLUTION
            ++histogram[0];
        }
        else 
        if(number <= 20.0) {
            ++histogram[1]; 
        }
    
        etc.
    
    }
    
  2. Write a method toString to construct and return a String representation of the histogram (similar to the toString method of the Arrays class).

    public static String toString( int [] histogram ) {
        ....
    
        // This method must also use a loop that iterates 
        // over the histogram array to form the string
        // representaion of it.
    
        // The histogram can be visualzed as a series of
        // buckets, where each bucket represents one range
        // of the histogram:
           [0..10]: **** 
           (10..20]: ** 
           (20..30]: *** 
           (30..40]: * ... ]
    
        // The string returned should only contain the string representation
        // of the histogram and no other verbeage. It should function
        // like the toString method of the Array class but specific to
        // creating a histogram.
    
        // You may want to create an instance of the
        // StringBuilder class to assist you in this method.
        // Follow the code in the method getHeaderAsString
        // as a guide.
    }
    
  3. Write a method to determine if the integer passed to the method is in the legal range of valid inputs as specified by the static variables of the class, LOWER_BOUND and UPPER_BOUND:

    public static boolean validInput( double num ) {
        ....
    }
    
  4. Write a method that performs the user input. This method accepts an object of the scanner class, uses this object to perform user input by calling the apporopriate methods, and returns an array of the floating point values that were input. The Scanner object passed to this method should be created in the main method and passed to this method. The array returned from this method will be passed to the calculateHistogram method to create the Histogram. See the template code provided.

    public static double[] inputNumbers( Scanner scan ) {
        ....
    }
    

A few guidelines on user input:

* The user may input at most the maximum amount of numbers as specified
  by the `static` variable `MAX_NUMBERS`. Each number entered must be 
  between the valid bounds. This method should do error checking and
  reporting accordingly: Any input number outside the range
  is an input error, and the program should report the error and ask for a
  correct input.

* Consider the correct data types and methods of Scanner class that
  should be used.

* Your method will also need to determine how to keep
  track of how many numbers you have read in (i.e. input) so you do not
  exceed the maximum numbers to be entered.

* To not force the user to enter the maximum inputs allowed,
  you can make use of a *sentinal* value to signal end of intput. For example, you
  continue to accept input values until the value as specified
  by the `static` variable `SENTINAL` is entered (or off course
  until the maximum number of inputs has been reached). The logic here
  can get more complicated then you may think. Think through all the possible
  cases carefully.

* The array that this method returns should only be as large as the number
  of inputs read in.
  1. Finally, make sure you use the static variables which have been declared in the template provided. You should NOT have the numbers 20 or 10 occur anywhere in your program except in the declarations of these variables. You MUST do this so that if you decide to change one of these, you don’t have the “multiple update” problem. Here is an example of what you should use:
    public class Histogram {
    
        public static final int SENTINAL = -999;   // sentinal value used to signal end of input
        public static final int MAX_NUMBERS = 20;  // maximum number of numbers to input
        public static final int NUM_BINS    = 10;  // number of bins in range [0..100]
    
        // UPPER_BOUND  to represent the largest possible number in the range
        // LOWER_BOUND  to represent the smalles possible number in the range
        // BIN_SIZE     how many different values fall into each bin
    
        public static void main(....) etc.
    

Important

The values assigned to MAX_NUMBERS and NUM_BINS determine the range of values represented by each bin. For example:

number of bins     size of bins
    10                10     i.e.,  [0..10], (10..20], ...., (90..100]
     5                20     i.e.,  [0..20], (20..40]  ...., (80...100]
     2                50     i.e.,  [0..50], (50..100]

It is clear when NUM_BINS is 10, then so is the size, but this is a coincidence. Your program should determine the size (i.e. range) of each bin. Consider how another variable could be used here.

Don’t worry about the possible error when the number of bins does not divide evenly into 100 (we’ll run your program as is, and won’t change the number of bins from 10 to something else).

Sample executions of Histogram.java:

Sample Run #1 histogram-console-1.png

This sample run uses -1 as the sentinal value, you just need to use the variable as set by the class variable SENTINAL

Problem 6: BigInt: a class for large integers

30 points total; individual-only

Overview
In Java, the int data type can only represent integers from -2,147,483,648 to 2,147,483,647. This isn’t big enough to
store very large numbers—for example, the population of Earth (approximately 7.53 billion as of 2017)—or to compute n! for large values of n.

The Java libraries contain a built-in class called BigInteger to handle arbitrarily large integers. In this problem, you will write your own class called BigInt that has a subset of the functionality of the built-in Java class.

Representing integers using an array of digits
Each BigInt object will use an arrays of integers, where each element of the array represents a single digit of the integer.

We will design the class to handle non-negative integers with up to 20 digits, and thus the array will have a length of 20.

For example, the integer 57431 would be represented using this array:

{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 4, 3, 1}

Note that:

As another example, the integer for 7.53 billion would be represented using the array

{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 5, 3, 0, 0, 0, 0, 0, 0, 0}

Task 0: Download and review our starter code
Begin by downloading the following starter file: BigInt.java

Open this file in your IDE and review its contents. We have given you:

Important guidelines

  1. You may not add any additional fields to the BigInt class.

  2. You may not use any variables of type long, any of Java’s built-in classes for numbers (Long, BigInteger, etc.), or any of Java’s built-in collection classes (e.g., ArrayList).

Task 1: Add your first additional constructor
Add an additional constructor with the following header:

public BigInt(int[] arr)

It should use the contents of the array that is passed in as the basis of the new BigInt object. For example, we should be able to create a BigInt for 57431 as follows:

int[] arr = {5, 7, 4, 3, 1};
BigInt val = new BigInt(arr);

Suggested approach:

Important: Make sure that the digits field ends up referring to a new array of length SIZE. You should not make it refer to the array that is passed in.

Note: You won’t be able to fully test this constructor until after you have completed the next task.

Task 2: Add two methods useful for testing
You should now add the following methods:

Once these two methods are defined, you can—and should!—uncomment the first few tests in the main method, and run the class to confirm that you get the correct results.

Task 3: Add the remaining methods
You are now ready to implement the remaining methods of the class. After adding a given method, uncomment the corresponding tests in the main method to test it.

Algorithms
The algorithms for manipulating these big integers are simply computational versions of the procedures that we would use to add, compare, or multiply integers “on paper”.

For example, to add two arrays of digits, we must go from right to left (i.e. least significant digit to most significant digit) adding the digits and keeping track of the carry. For example, to add 57339 to 4598, the process looks something like this:

carry:           1   0   1   1     
            -----------------------+
       ...   0 | 5 | 7 | 3 | 3 | 9 |
            -----------------------+
            -----------------------+
       ...   0 | 0 | 4 | 5 | 9 | 8 |
            -----------------------+
            -----------------------+
sum:   ...   0 | 6 | 1 | 9 | 3 | 7 |
            -----------------------+

Note that addition will result in overflow (creating a number with more than 20 digits) if you need to add the digits in position 0 of the two arrays and if their sum plus any incoming carry value produces a value that is greater than or equal to 10.

We encourage you to:


Submitting Your Work

Submission Checklist:

Submitting your work for Part I

Submit your ps3_partI.pdf file using these steps:

  1. If you still need to create a PDF file, open your file on Google Drive, choose File->Download as->PDF document, and save the PDF file on your machine.

  2. Click on the name of the assignment in the list of assignments on Gradescope. You should see a pop-up window labeled Submit Assignment. (If you don’t see it, click the Submit or Resubmit button at the bottom of the page.)

  3. Choose the Submit PDF option, and then click the Select PDF button and find the PDF file that you created. Then click the Upload PDF button.

  4. You should see a question outline along with thumbnails of the pages from your uploaded PDF. For each question in the outline:

    • Click the title of the question.
    • Click the page(s) on which your work for that question can be found.

    As you do so, click on the magnifying glass icon for each page and doublecheck that the pages that you see contain the work that you want us to grade.

  5. Once you have assigned pages to all of the questions in the question outline, click the Submit button in the lower-right corner of the window. You should see a box saying that your submission was successful.

Submitting your work for Part II

You should submit only the following files:

Here are the steps:

  1. Click on the name of the assignment in the list of assignments. You should see a pop-up window with a box labeled DRAG & DROP. (If you don’t see it, click the Submit or Resubmit button at the bottom of the page.)

  2. Add your files to the box labeled DRAG & DROP. You can either drag and drop the files from their folder into the box, or you can click on the box itself and browse for the files.

  3. Click the Upload button.

  4. You should see a box saying that your submission was successful. Click the (x) button to close that box.

  5. The autograder will perform some tests on your files. Once it is done, check the results to ensure that the tests were passed. If one or more of the tests did not pass, the name of that test will be in red, and there should be a message describing the failure. Based on those messages, make any necessary changes. Feel free to ask a staff member for help.

    Note: You will not see a complete Autograder score when you submit. That is because additional tests for at least some of the problems will be run later, after the final deadline for the submission has passed. For such problems, it is important to realize that passing all of the initial tests does not necessarily mean that you will ultimately get full credit on the problem. You should always run your own tests to convince yourself that the logic of your solutions is correct.

  6. If needed, use the Resubmit button at the bottom of the page to resubmit your work. Important: Every time that you make a submission, you should submit all of the files for that Gradescope assignment, even if some of them have not changed since your last submission.

  7. Near the top of the page, click on the box labeled Code. Then click on the name of each file to view its contents. Check to make sure that the files contain the code that you want us to grade.

Important

  • It is your responsibility to ensure that the correct version of every file is on Gradescope before the final deadline. We will not accept any file after the submission window for a given assignment has closed, so please check your submissions carefully using the steps outlined above.

  • Make sure to use these exact file names as specified or we will not be able to find your files and grade them.

  • Make sure that you do not try to submit a .class file or a file with a ~ character at the end of its name.

  • If you make any last-minute changes to one of your Java files (e.g., adding additional comments), you should compile and run the file after you make the changes to ensure that it still runs correctly. Even seemingly minor changes can cause your code to become unrunnable.

  • If we are not able to compile your program, you will not receive any credit for that problem submission.