HW9 Assignment

What to do?

You must turn in your program by Friday, December 3rd, 2021 at 11:59pm.

In this assignment, you are to implement a binary tree data structure to use the "generics" feature of Java

Your Tree.java code will implement a binary tree similar to that in hw8, but you will implement the binary tree as a binary tree on disk. You are to rewrite Driver.java to allow for input commands to come from an ASCII file or via the keyboard by default.

You will need to complete the definition for the UCSDStudent class in your Driver.java.

The TNode methods to write in Tree.java:

public TNode (Whatever element, String caller)
public TNode (long position, String caller)
public void read (long position)
public void write ()
private Whatever insert (Whatever element, PositionBox positionInParentBox)
private Whatever lookup (Whatever element)
private Whatever remove (Whatever element, PositionBox positionInParentBox, boolean fromSHB)
private Whatever replaceAndRemoveMin (PositionBox positionInParentBox)
private Whatever setHeightAndBalance (PositionBox positionInParentBox)

The Tree methods to write in Tree.java:

public Tree (String datafile, Whatever sample, String caller)
public Whatever insert (Whatever element)
public boolean isEmpty ()
public Whatever lookup (Whatever element)
public Whatever remove (Whatever element)
public void write ()
public void resetRoot ()

Code to write in Driver.java:

Add appropriate public class methods for UCSDStudent similar to Variable class from Variable.java
Fill in code for the f case

To update your Driver.java for ASCII file input:

- Add the statement import java.io.* at the beginning of your file if it is not in it. - We have decleared a Reader of the type InputStreamReader and assign it to System.in for you.
- We have declare an Writer of the type FlushingPrintWriter and assign it to System.out for you.
- 'f' is the Driver command for file input. The input following 'f' is the name of the file for command input.
- file input terminates (when your command == EOF) with either
    a) end of command input file
    b) command such as "f newfile" within command input file.
If condition "a)" occurs, you need to continue reading from System.in.
If condition "b)" occurs, you need to read instead from "newfile."
- to start reading from a file for input, you'll need to assign your Reader to new FileReader (buffer) where "buffer" is a character array holding the name of the file. This works because FileReader is a derived class from Reader.
- to terminate input from the file, you'll need to reassign the Reader back to a new InputStreamReader reading from System.in (only if not reading from KEYBOARD)
- make analogous changes with the Writer so that when input comes from an ASCII file, output goes to "/dev/null."
HINT: Check if there is any more input with Reader. Then check if input is coming from keyboard, if so then stop reading in input, otherwise, reset Reader and Writer.

To update your Driver.java to display cost and operation add the following lines before jettison the symtab:

if (symtab.getOperation () != 0){
    System.out.print ("\nCost of operations: ");
    System.out.print (symtab.getCost());
    System.out.print (" tree accesses");

    System.out.print ("\nNumber of operations: ");
    System.out.print (symtab.getOperation());
    System.out.print ("\nAverage cost: ");
    System.out.print (((float) (symtab.getCost ())) / (symtab.getOperation ()));
    System.out.print (" tree accesses/operation\n");
} else{
    System.out.print ("\nNo cost information available.\n");
}

How to update your Tree.java for BINARY file input and output:

- the fields root, left, and right are now of type "long" since they now represent positions within a binary file.
- the binary datafile can be opened via the Tree constructor initialization through...

fio = new RandomAccessFile (datafile, "rw");

where "datafile" is the const char pointer parameter that contains the name of the binary file.
- the binary file can be closed via the Tree destructor through...

fio.close ();

- However, because methods in java.io.* throw excpetions, you would need to use try catch blocks to handle them.

try {
    fio.close ();
}
catch (IOException ioe) {
    System.err.println ("IOException in Tree's jettison");
}

- you'll have to use the following calls (or something like them) throughout your Tree and TNode constructor, destructor, Read and Write code:

long position = fio.getFilePointer ();// position in file

fio.seek (BEGIN); // seek to a the beginning of the file
fio.seek (position); // seek to a position in the file
fio.seek (fio.length ()); // seek to the end of the file
fio.writeLong (root); // write a long in the file at the current file pointer position

data.write (fio);
data.read (fio);

- within your searchTree and setHeightAndBalance, you'll have code like (no logic implied):

write () // call the write function of TNode to write the current node to the disk
TNode readNode = new TNode (positionInParentBox.position, "searchTree " + action); // reads

For all methods in this assignment, make sure to include code to display the corresponding catastrophic error and debug messages.

Function Descriptions

Note: The non-constant reference parameters (noted in red in Function Descriptions) are potentially updated within the body of that method.

TNode read constructor:

Called when reading a TNode present on disk into memory. You would need to first create a copy using the sample belongs to the tree and assign it to be the data of the newlly created TNode.

  • Values of position are expected to be the offset in the datafile corresponding to the position of the TNode we wish to read into memory.
  • Values of caller are expected to be a string describing where this method is called from.
  • TNode write constructor:

    Called when creating a TNode for the first time:

  • Values of element are expected to be the data to be stored in the TNode. They must be of the same type as the rest of the objects present in the Tree.
  • Values of caller are expected to be a string describing where this method is called from.
  • TNode's insert: /* called from Tree's insert and TNode's searchTree*/

    Inserts an element into the binary tree. Returns the data inserted.

  • Values of element are expected to be the data to be stored in the TNode. They must be of the same type as the rest of the objects present in the tree.
  • Values of positionInParentBox are expected to be a reference to a wrapper object that holds the TNode position in the parent TNode that was used to get to the current TNode's offset in the datafile.
  • TNode's read: /* called from TNode's read constructor, and a loop-based Lookup */

    Reads a TNode which is present on the datafile into memory. The TNode is read from position. The TNode's information in the datafile overwrites this TNode's data. In order to read in the data field of the TNode, because the read constructor has generated a data of the correct type, you can call the read method of your data to read in the information of data from the disk file.

  • Values of position are expected to be the offset in the datafile corresponding to the position of the TNode we wish to read into memory.
  • TNode's remove: /* called from Tree's Remove, SetHeightAndBalance, and searchTree */

    Removes the matching data from the binary tree. Returns true or false indicating success of removal.

  • Values of element are expected to be a reference to the data that identifies the element to remove.
  • Values of positionInParentBox are expected to be a reference to a wrapper object that holds the TNode position in the parent TNode that was used to get to the current TNode's offset in the datafile.
  • Values of fromSHB are expected to be true or false, keeping track of whether or not Remove was called from SetHeightAndBalance so that Remove can determine whether or not to call SetHeightAndBalance.
  • searchTree: /* called from Tree's remove, insert, lookup, setHeightAndBalance and replaceAndRemoveMin */

    We have provided you this method.
    Read in a TNode from the position that is pointed by the positionInParentBox, and call the appropriate functions on the newly read in node.
    This method has already implemented for you, just figure out where to call it.

  • Values of element are expected to be a reference the data that either will be inserted in or identifes the TNode to be removed.
  • Values of positionInParentBox are expected to be a reference to a wrapper object that holds the TNode position in the parent TNode that was used to get to the current TNode's offset in the datafile.
  • Values of action are expected to be a string among "insert", "lookup", "RARM" and "remove". This string is the corresponding function that you should call after read in a node from the datafile.
  • replaceAndRemoveMin: /* called from searchTree */

    Called when removing a TNode with 2 children, replaces that TNode with the minimum TNode in it's right subtree to maintain the Tree structure.

  • Values of positionInParentBox are expected to be a reference to a wrapper object that holds the TNode position in the parent TNode that was used to get to the current TNode's offset in the datafile.
  • setHeightAndBalance: /* called from TNode's Remove, TNode's Insert, and ReplaceAndRemoveMin */

    Updates the height and balance of the current TNode.

  • Values of positionInParentBox are expected to be a reference to a wrapper object that holds the TNode position in the parent TNode that was used to get to the current TNode's offset in the datafile.
  • TNode's write: /* called from TNode's write constructor, and functions which update the TNodes in datafile */

    Writes this TNode object to disk at position in the datafile.

    Tree Constructor:

    Allocates the tree object. Checks the datafile to see if it contains Tree data. If it is empty, root and occupancy fields are written to the file. If there is data in the datafile, root and occupancy fields are read into memory. HINT: You will want to seek before writing or reading anything to / from disk.

  • Values of datafile is the name of the datafile that you would need to write to the disk.
  • Values of sample are expected to be a sample object that will be stored in the tree.
  • Values of caller are expected to be a string describing where this method is called from.
  • Tree's write: /* called from Tree's jettison */

    Write the field root and occupancy fields at the beginning of the diskfile.

    isEmpty:

    Return whether the Tree is empty.

    Tree's jettison:

    Jettisons memory associated with the Tree and write the tree back to the disk. Then you would need to close the fio, and jettsion the sampleNode.

    Tree's insert:

    Inserts an element into the binary tree. Inserts at the root TNode if Tree is empty, otherwise delegates to TNode's insert by calling the searchTree method on the sampleTNode. Returns the data inserted.

  • Values of element are expected to be the data stored in the TNode. They must be of the same type as the rest of the objects present in the tree.
  • Tree's lookup:

    Searches for an element in the Tree. This function may be recursive or loop-based. If you use a recursive algorithm, you should make a TNode Lookup method by calling searchTree method on the sampleTNode (as in the remove and insert functions).

  • Values of element are expected to be the data stored in the TNode. They must be of the same type as the rest of the objects present in the tree.
  • Tree's remove:

    Removes an element from the Tree. Delegates to TNode's Remove by calling searchTree when Tree is not empty. Returns the removed object, null if the removal failed.

  • Values of element are expected to be the data stored in the TNode. They must be of the same type as the rest of the objects present in the tree.
  • resetRoot:

    Resets the root datafield of this tree to be at the end of the datafile. This should be called when the last TNode has been removed from the Tree.

    Implementation Restrictions

    TNode's insert and TNode's remove should be implemented via recursion.

    TNode's lookup can be either loop-based or recursive.

    Grading breakdown

    Comments/Documentation:
    20%
    Style:
    14%
    Correctness/Execution:
    66%

    How to get started?

    Enter the following commands into the terminal.

    cd ~/../public/hw9
    make install
    cd ~/hw9/java
    mv Tree.java.empty Tree.java
    mv Driver.java.empty Driver.java