Problem Solving Assignment 1

Due: Thursday of week 2 (1/15/2009) at 11:59pm


NEW! We have added the sample solution to the assignment, that you can run to test that your code behaves correctly. See below.

This assignment is very similar to how a search engine works. A search engine will read words on every page that it will include in the searches. It will store information about the words for each page. You can write a search engine to do this in many ways. One way is by seeing how many times each word appears on each page. With this information stored for each page, when a search is performed, the pages are ranked based on the number of times that word appears on the pages. For example, if we were searching for "hello" and all 5 pages that we have indexed have the word "hello" in it, but the first page had it 5 times, the second page had it 4 times, and so on, the pages should be printed to the user from page 1 to page 5 in that order, since page 1 has "hello" more frequently. This is a little more in depth than what we are doing in this class. In this class, we are going to be looking through text and indexing how many times each letter appears in the text, then writing our results to a new file. This could tell us what symbols are seen more frequently.

This homework requires skills in File I/O, from chapter 12 in Guzdial. You will need to write the following classes:

  1. LetterFrequencies -- Create a class LetterFrequencies that will contain the counts of each individual letter in the input file.
  2. FrequencyCounter -- Create a class FrequencyCounter that will contain the 'main' method and perform the File I/O to compute the letter frequencies (by using a LetterFrequencies instance).

Details

You must put a comment at the top of your code that specifies your: real name, login name, email address, and PID. You must also include a two-to-four sentence description of the program. Use a multi-line comment (/* */). YOU MUST DO THIS FOR EVERY JAVA FILE!

Comments Comments in the style detailed in the book ARE required. Your code must be properly commented to receive full credit.

LetterFrequencies

This class is designed to count the number of times each letter of the alphabet occurs in the input file. We are only concerned with letters, and we make no distinction between uppercase or lowercase letters (i.e. if you see an 'A' character or an 'a' character in the file, those should both add 1 to the same counter). Since we are only counting letters, we can store these counters in an integer array that has 26 elements. We can define a mapping between letters and array indexes as follows:

A=0, B=1, ..., Y=24, Z=25

The LetterFrequencies class will have an integer array field called 'frequencies', which will store the counts of the letters seen in the file. The count for 'A' will be stored in index 0, the count for 'B' will be stored in index 1, etc.

The LetterFrequencies class should have the following methods:

  1. LetterFrequencies (constructor) - This should take no parameters, and initialize the 'frequencies' instance field.
  2. letterToIndex - This method should take in a char value and if it is a letter, return the array index it corresponds to. If the char is not a letter, this should return -1. For example,

    letterToIndex('A') == 0
    letterToIndex('a') == 0
    letterToIndex('e') == 4
    letterToIndex('z') == 25
    letterToIndex('?') == -1
    letterToIndex('#') == -1

    (hint: this method might be useful for implementing other methods!)

  3. countLetter - This should take in a char value and, if that value is a letter (i.e. one of 'a','b',...,'z','A','B',...,'Z'), it will add 1 to the appropriate counter. If the value is not a letter, it is ignored. This method should have no return value.
  4. getFrequency - This should take in a char value and if it is a letter, return the value of the current counter for that letter. If the char is not a letter, this should return -1.

FrequencyCounter

This class is designed to perform the File I/O and use a LetterFrequencies instance to keep track of the frequencies of the letters in the file. After reading the entire input file and building the LetterFrequencies object, this class will output the list of frequencies to another file in a format detailed below.

The FrequencyCounter class should have the following methods:

  1. main - This will be the starting point for the whole program and will call other methods to read the input file, calculate the letter frequencies, and output the results file.
  2. inputFrequencies - This method takes no parameters, and opens the file named "input.txt" for reading using a BufferedReader wrapped around a FileReader. This method will create a LetterFrequencies object and then read the input file. For each character in the input file, the frequency for that character in the LetterFrequencies object should be updated. After reaching the end of the file, the BufferedReader should be closed, and the LetterFrequencies object should be returned.

    The format of the input file will be as follows:

    <number of lines>
    <line 1>
    <line 2>
    ...
    So an example input file could look like:
    3
    This is a Haiku.
    Five, then seven, then five more.
    Try it out yourself.

    The method of BufferedReader that you will use to read text from the file is readLine(). This will read an entire line of the file and return it as a single String. After you read the first line of the input file, you will need to transform that String into an int using the static method Integer.parseInt, which takes a String parameter and returns an int. Then once you have this int value (call it N, for example), you can write a for-loop that will read in the remaining N lines of the file.

  3. outputFrequencies - This method should take in a LetterFrequencies object and print a description of the frequencies to an output file. You will open the output file with a BufferedWriter wrapped around a FileWriter, and the output file name will be "output.txt".

    The format of the output file will be as follows:

    a <count for 'a'>
    b <count for 'b'>
    ...
    z <count for 'z'>
    An important note is that there should NOT be a line for any letter that has a count of 0. Here is the output file for the example input file above:
    a 2
    e 8
    f 3
    h 4
    i 6
    k 1
    l 1
    m 1
    n 3
    o 3
    r 3
    s 4
    t 6
    u 3
    v 3
    y 2

    The letters in this file must be lowercase, and there should be exactly one space between the letter and the count of its frequency.

    After you have finished writing the output file, the BufferedWriter must be closed (by calling the close() method on it). The outputFrequencies method should have no return value.

Handling Exceptions

This assignment will deal with methods that can potentially cause exceptions to be thrown, and your code needs to handle this appropriately.

Both the reading and writing can cause exceptions to be thrown. This will occur if the system cannot open or locate the filename you give it, or if it cannot read/write the file. Because of this, you need to put a try/catch block around those calls.

In this assignment, any exception that occurs is fatal, which means that you should terminate the program if one occurs. The way to terminate a java program is to call System.exit(1). This is not a crash, it just tells the program to immediately halt.

When reading the input file, the catch blocks should have the following behavior:

(the FileNotFoundException block must come first!)

When writing the output file, the catch block should have the following behavior:

(there is no FileNotFoundException thrown for output)

Testing/Grading

Since this program is more advanced than some others you may have written, you should put extra time into testing your code to ensure that it can handle all of the important cases. Specifically, we will DEFINITELY be testing the following input scenarios:

Sample solution

In order to help you know what the correct behavior of your program should be, we have provided a teacher's solution that you can run. If you are on ieng6, then you should be able to type "runsamplePSA1", and this will run the reference version of FrequencyCounter.

A word about Java chars

This assignment will require you to repeatedly test whether or not a given char value is a letter. An obvious (and tedious) way to do this is to have a 26-pronged if-statement that says:

if (mychar=='a') {
   ...
} else if (mychar=='b') {
   ...
} ...

A much better way to approach this is to rely on the fact that Java chars are actually numbers. In Java, every char has an associated numerical value. You can get the numerical value of a char by casting it to an int. The numbers for each char are somewhat arbitrarily chosen, except that the uppercase letters are assigned consecutively in the range 65-90, and the lowercase letters are assigned consecutively in the range 97-122.

Given this information, you should ask yourself two questions that will be relevant to this assignment:

  1. How can I quickly and easily test to see if a char is a letter?
  2. Once I know a given char is a letter, how could I quickly and easily convert that letter to an array index in the range 0-25?

Sample input/output

In addition to writing the two classes described above, you will be required to submit one sample input file and one sample output file. The input file can contain any text that you like, but it must be formatted according to the style described above. The input file that you turn in must be named "input.txt".

The sample output file is just the result of running YOUR code on the sample input file you provide. It should be named "output.txt", and should be formatted according to the style described above.

Turning in your homework

We will be using a Bundle script to do automated turnin for this assignment.

On ieng6 somewhere under your home directory you need to make a folder called PSA1. All of your files for this assignment must go in this folder. The files you need to turn in are:

(You do NOT need to copy bookClasses into this folder. We're not using bookClasses for this assignment.)
When you have completed the assignment and have all of these files in the PSA1 folder, you should cd into that folder and then run the bundleP1 command.


Here are several examples of the expected input/output behavior that your code should have. For each of these examples, your code should print EXACTLY the same output.

(when there is no input.txt file)
> java FrequencyCounter
Error opening file


(when input.txt file does not start with a number)
> java FrequencyCounter
Error reading from file


(suppose that input.txt contains the following text
1
aAbB
before the program is run)
> java Frequency
>
(program has no printed output)
(final contents of output.txt
a 2
b 2
after the program is run)

(suppose that input.txt contains the following text
3
a b c d e
1 2 3 4 5
z y x w v
before the program is run)
> java Frequency
>
(program has no printed output)
(final contents of output.txt
a 1
b 1
c 1
d 1
e 1
v 1
w 1
x 1
y 1
z 1
after the program is run)