8.3

#### Assignment 6: Mutable data

Goals: Constructing cyclic data; using mutation to design data structures; using mutation to solve algorithmic problems.

##### Instructions

As always, be very careful with your naming conventions.

The submissions will be organized as follows:

• Homework 6 Problem 1: The Registrar.java file.

• Homework 6 Problem 2: The Deque.java file.

Due Date: Friday Mar 4, 9:00pm.

Note: you can begin working on the Registrar problem using concepts we’ve already covered in class. The Deque problem will make more sense possibly after Monday’s lecture, and definitely after Wednesday’s lecture: you may want to read ahead in the lecture notes to get a head-start on that problem.

### Problem 1:The Registrar’s Office

The registrar’s office maintains a great deal of information about classes, instructors, and students. Your task in this problem is to model that data, and implement a few methods on it. We deliberately do not give you the class diagram for this problem: from the description below, you should properly design whatever classes you think are relevant. Please use generic lists (i.e. IList<T>) for this problem.

##### 6.1Data constraints
• A Course consists of a name (a String), an Instructor named prof, and a list of Students named students. No Course can be constructed without an Instructor available to teach it. Hint: Do you think that you should provide a list when constructing a Course? Why or why not?

• An Instructor has a name and a list of Courses named courses they teach. Instructors are initially constructed without any Courses to teach.

• A Student has a name, an id number (an int), and a list of Courses they are taking. Students are initially constructed without taking any Courses.

• It should always be the case that any Student who is enrolled in a Course should appear in the list of Students for that Course, and the Course should likewise appear in the Student’s list of Courses.

• It should always be the case that the Instructor for any Course should have that Course appear in the Instructor’s list of Courses.

##### 6.2Methods and examples to design
• Design a method void enroll(Course c) that enrolls a Student in the given Course. Design any helper methods as appropriate.

• Design a method boolean classmates(Student s) that determines whether the given Student is in any of the same classes as this Student.

• Design a method boolean dejavu(Student s) that determines whether the given Student is in more than one of this Instructor’s Courses.

• Construct example data consisting of at least five Students, at least four Courses and at least two Instructors. Test all your methods thoroughly.

Hint: you will at minimum need to design some kind of sameness-checking method for Students. We are not giving you any guarantees about how the classes in your program might be used or instantiated, so you must figure out what information should be used to define sameness, and justify your reasoning.

### Problem 2:Who’s On Deque?

We would like to build a generic list in such a way that we can start either at the front, or at the back, and move through the list in either direction. Here is an example of such a list (shown here with Strings), drawn as an object diagram:

              +---------------+
| Deque<String> |
+---------------+
| header ----------+
+---------------+  |
+--------------+
|
v
+------------------+
+---------->| Sentinel<String> |<-------------------------------------------+
|           +------------------+                                            |
|       +---- next             |                                            |
|       |   | prev ------------------------------------------------+        |
|       |   +------------------+                                   |        |
|       |                                                          |        |
|       v                                                          v        |
| +--------------+   +--------------+   +--------------+   +--------------+ |
| | Node<String> |   | Node<String> |   | Node<String> |   | Node<String> | |
| +--------------+   +--------------+   +--------------+   +--------------+ |
| | "abc"        |   | "bcd"        |   | "cde"        |   | "def"        | |
| | next ----------->| next ----------->| next ----------->| next ----------+
+-- prev         |<--- prev         |<--- prev         |<--- prev         |
+--------------+   +--------------+   +--------------+   +--------------+

Every Deque has a header that consists of the Sentinel node. This header field does not change, but the fields within the Sentinel node (that provide the links to the first and the last item in the Deque) can and will change.

Each data Node has two links, one to the next item in the list and one to the previous item in the list.

The Sentinel node is always present. It also has next and prev fields, which are consistently updated to link to the head of the list and to the tail of the list, respectively.

The Sentinel and Node classes both inherit from a common superclass (shown below), because the next or prev item after any given node may be either a data-carrying Node or the Sentinel marking the end of the list.

The list shown above has four data-carrying nodes and the lone sentinel. The empty list has just the Sentinel, and its links to the first and to the last item just reference the Sentinel node itself. So an empty list would have the following object diagram:

     +---------------+
| Deque<String> |
+---------------+
| header ---------+
+---------------+ |
|
+--------+
+----+        |     +----+
|    |        |     |    |
|    V        V     V    |
|  +------------------+  |
|  | Sentinel<String> |  |
|  +------------------+  |
+--- next             |  |
| prev ---------------+
+------------------+

The class diagram for these classes is as follows:
        +--------------------+
| Deque<T>           |
+--------------------+
| Sentinel<T> header |-+
+--------------------+ |
|
+--------------------------+
|
|
|      +---------------+
|      | ANode<T>      |
|      +---------------+
|      | ANode<T> next |
|      | ANode<T> prev |
|      +---------------+
|         /_\     /_\
|          |       |
|     +----+       |
V     |            |
+-------------+      +---------+
| Sentinel<T> |      | Node<T> |
+-------------+      +---------+
+-------------+      | T data  |
+---------+

We have an abstract class ANode<T>, which can be either a Sentinel<T> node or an actual Node<T> containing data. We use an abstract class here instead of an interface because we need to share the next and prev fields.

With one caveat, below.

Moreover, because all ANodes are contained and hidden within the Deque, no external code needs to know about it: they are implementation details rather than a publicly visibly datatype definition.

##### 6.1Data definitions and examples
• Define the classes ANode<T>, Node<T>, Sentinel<T>, and Deque<T>.

• For Sentinel<T>, define a constructor that takes zero arguments, and initializes the next and prev fields of the Sentinel to the Sentinel itself.

• For Node<T>, define two constructors: the first one takes just a value of type T, initializes the data field, and then initializes next and prev to null. The second convenience constructor should take a value of type T and two ANode<T> nodes, initialize the data field to the given value, initialize the next and prev fields to the given nodes, and also update the given nodes to refer back to this node. Throw an IllegalArgumentException in this constructor if either of the given nodes is null. (You can use if (theNode == null) { ... } to test for null-ness.) Note carefully the order of the arguments in this constructor! The order should match the class diagram above; getting the order wrong will result in oddly “backwards” lists.

• For Deque<T>, define two constructors: one which takes zero arguments and initializes the header to a new Sentinel<T>, and another convenience constructor which takes a particular Sentinel value to use.

• Make examples of three lists: the empty list, a list of Strings with the values ("abc", "bcd", "cde", and "def") shown in the drawing at the beginning of this problem, and a list with (at least) four values that are not ordered lexicographically. (Hint: If you’ve defined your constructors correctly, you shouldn’t need to use any explicit assignment statements in your examples class!)

(Make more examples as needed to test the methods you define.)

Name your examples class ExamplesDeque, and your first three examples deque1, deque2 and deque3.

##### 6.2Methods
• Design the method size that counts the number of nodes in a list Deque, not including the header node. (I.e., just count the Nodes and not the Sentinel.)

• Design the method addAtHead for the class Deque that consumes a value of type T and inserts it at the front of the list. Be sure to fix up all the links correctly!

• Design the method addAtTail for the class Deque that consumes a value of type T and inserts it at the tail of this list. Again, be sure to fix up all the links correctly!

• Design the method removeFromHead for the class Deque that removes the first node from this Deque. Throw a RuntimeException if an attempt is made to remove from an empty list. Be sure to fix up all the links correctly! As with ArrayList’s remove method, return the item that’s been removed from the list.

• Design the method removeFromTail for the class Deque that removes the last node from this Deque, analogous to removeFromHead above. Again, be sure to fix up all the links correctly!

• You probably have duplicate code in addAtHead, addAtTail, removeFromHead and removeFromTail. Revise your code to abstract out any duplication into helper methods — most likely, helper methods on ANode<T> and its subclasses.

• These last two methods slightly break our claim above. Consequently, they’re not an ideal design for our interface, but they are better practice for the skills on this assignment than a more robust OOD approach to them.

Design the method find for the class Deque that takes an Predicate<T> and produces the first node in this Deque for which the given predicate returns true. (The Predicate<T> interface is predefined for us, just like java.util.function.Function<A, R> was from the previous homework. It is essentially the same as the IPred<T> interface from lecture, though the method name is slightly different:
 // Represents a boolean-valued question over values of type T interface Predicate { boolean test(T t); }

You’ll need to import this interface, and then define several classes that implement it, to test your code.)

If the predicate never returns true for any value in the Deque, then the find method should return the header node in this Deque, rather than return null. (Hint: think carefully about the return type for find!)

• Design the method removeNode for the class Deque that removes the given node from this Deque. (Unlike removeFromHead or removeFromTail, this method does not need to return anything. Why?) If the given node is the Sentinel header, the method does nothing. (Hint: think again about the return type from find!) If you’ve revised your code to remove duplication as suggested above, this method should be very short and simple to implement.