Harvey Mudd College
Computer Science 60
Assignment 3, Due Monday, February 14, by 11:59pm

From Racket to Java...

Assuming you took CS5, at this point in CS60 youve worked with (at least) three programming languages: Python, HMMM, and Racket. This week - and in the coming weeks - we will add Java to that list (if it's not already there).

In addition, over the next two weeks you'll build your own task-specific language that will be able to handle arbitrary multiplication-based conversions from one unit to another -- and account for error propagation, too. To do this, your application will be able to navigate within arbitrarily large graphs of unit-conversion data. We're calling this application a unit-calculator, or unicalc.

In the spirit of both Racket and Java, you'll prototype your application in Racket this week. In addition, this week you'll build the fundamental data structure for a Java implementation of unicalc, which you'll complete next week. This data structure will be the OpenList class, in which you'll create Racket-like linked lists that can be naturally handled recursively. Thus, OpenList "shows off" Java's data-structuring capabilities without sacrificing the generality and power of Racket/Scheme.


Submitting your code

Problems 1 and 3 this week may be done in pairs -- or individually, if you prefer. Problem 2 should be completed individually. Overall, you will submit 4 files for this week's three problems:


Problem 1: Unicalc, a unit-calculator in Racket

Pair or individual problem    You may work on this one either individually or in a pair. If you work in a pair, be sure to follow the CS 60 pair-programming guidelines.

30 Points

This problem asks you to write several Racket functions: together, they will implement a "unit-calculator" or unicalc application. A complete application will be able to convert any unit quantity to any compatible quantity by using an association list as a database of known conversions.

There are two files with which to start:

unicalc.rkt and unicalc-db.rkt

The first you will modify to implement unicalc; the second holds the unicalc unit-conversion database.

Quantity Lists    The fundamental structure used in unicalc is called a Quantity List. As a data structure, a Quantity List is a 4-element Racket list in which

For instance, the following would represent the typical value for Earth's acceleration due to gravity (with a small uncertainty of 0.01):
'(9.8 (meter) (second second) 0.01)

Generally speaking, a data structure is rather low-level: it specifies the particular way that language-specific and/or machine-specific constructs are used to organize data, as the above example indicates. An information structure, on the other hand, provides problem-specific capabilities that are independent of a particular application. To reinforce this distinction, we provide a make-QL function in Racket that creates a Quantity List. In addition, we provide the accessor functions get-quant, get-num, get-den, and get-unc, which return the individual pieces of a Quantity List. In order to compare and sort lists of symbols, we provide functions sym<? and sortsym.

In Racket, the difference between data structure and information structure is almost entirely one's point of view. After all, the different ways of organizing data are relatively limited!

The unit database    In the file unicalc-db.rkt is a unit-conversion database in the form of an association list - here are a few of its conversions:

(define unicalc-db 
  (list 
(list 'A                      (make-QL 1.0 '(ampere) '() 0.0))
(list 'faraday                (make-QL 96490.0 '(coulomb) '() 0.0))
(list 'farad                  (make-QL 1.0 '(coulomb) '(volt) 0.0))
(list 'fathom                 (make-QL 6.0 '(foot) '() 0.0))
(list 'hour                   (make-QL 60.0 '(minute) '() 0.0))
(list 'inch                   (make-QL 0.02539954113 '(meter) '() 0.0))
 ... ))
Not every unit is defined in terms of others in the database. Some are intentionally undefined: these are what we call basic units, e.g., second, kg, meter, and ampere.

When a quantity is normalized, it is converted entirely into basic units. Also, the database does not contain conversions from everything to everything: that would make it very large and difficult to maintain! Instead, the database can be used multiple times, e.g., to convert furlongs to miles to feet to inches to meters.

Unicalc's functions to write

A note on uncertainty-propagation: there are many online references that indicate how to propagate uncertainty through various functions. We used this reference from Harvard and this error-propagation calculator as a check for our results. Our uncertainties represent the size of one-standard-deviation within a Gaussian distribution around the quantity in our quantity list - note that we're not tracking relative uncertainties here, though the formulas below do compute them, as needed.

Where's convert?    With this toolkit of functions in place, unit-conversion turns out to be an application of division:

(define (convert from to)
  (norm-QL (divide from to)))
Feel free to include this and try it out!

More tests...    Here are a few more tests you might try. These combine your unit-calculation functions into composite operations:

(check-expect (equal-QLs? (make-QL 0.38735983 '(second) '() 0.030581039)
                          (divide (norm-QL (make-QL 3.8 '(m) '(s) 0.3))
                                  (norm-QL (make-QL 9.81 '(m) '(s s) 0.0))))
              #t)


(check-expect (equal-QLs? (norm-QL (make-QL 0.48 '(cm) '() 0.6327716))
                          (let* ((w (make-QL 4.52 '(cm) '() 0.02))
                                 (x (make-QL 2.0 '(cm) '() 0.2))
                                 (y (make-QL 3.0 '(cm) '() 0.6)))
                            (subtract (add (norm-QL x) (norm-QL y)) (norm-QL w))))
              #t)


(check-expect (equal-QLs? (norm-QL (make-QL 18.04 '(cm cm) '() 3.7119827))
                          (let* ((w (make-QL 4.52 '(cm) '() 0.02))
                                 (x (make-QL 2.0 '(cm) '() 0.2))
                                 (y (make-QL 3.0 '(cm) '() 0.6)))
                            (norm-QL (add (multiply w x) (power y 2)))))
              #t)

(check-expect (equal-QLs? (make-QL 5.1 '(meter) '() 0.360555127)
               (subtract (norm-QL (make-QL 14.4 '(meter) '() 0.3)) 
                         (norm-QL (make-QL 9.3 '(meter) '() 0.2))))
              #t)

(check-expect (equal-QLs? (make-QL 23.7 '(meter) '() 0.360555127)
               (add (norm-QL (make-QL 14.4 '(meter) '() 0.3)) 
                    (norm-QL (make-QL 9.3 '(meter) '() 0.2))))
              #t)

Congratulations! on creating an error-propagating unit calculator! You will extend this prototype implementation into a full-fledged language in the next assignment... (in Java!)

Submitting    Be sure to submit unicalc.rkt to the submission site; you do not need to submit unicalc-db.rkt.



Java

Problems 2 and 3 use Java and its unit-testing framework, JUnit. Remember that problem 2, Complex.java is an individual problem and problem 3, OpenList.java can be done in a pair or individually.

Remember, too, that you're welcome to use any Integrated Development Environment, these instructions will focus on Dr. Java, which has a relatively shallow learning curve. Either way, you'll be submitting only your plain-text source code (the .java files).

Java testing: JUnit

Because Java has a so strong an industry foothold as a development language, there are many tools available to help test Java code. We will use perhaps the most ubiquitous unit-testing framework, named JUnit. Fortunately, Dr. Java makes using JUnit quite straightforward: the basic framework is included with the IDE.

How to use JUnit for testing

This week we provide files of unit tests named ComplexTests.java (for problem 2) and OpenListTests.java (for problem 3). The goal for your Complex class will be a correct implementation for a complex-number class that satisfies the provided tets: you won't need to add more tests there. For OpenList, you will add more tests of your own.

Here is what a JUnit test looks like - although its syntax is Java, it's very much in the same spirit as Racket's check-expect. This one is from ComplexTests.java:

  /**
   * test of equals
   */
  public void test_equals() 
  {
    Complex actual = new Complex(42.0,60.0); // 42 + 60i
    Complex expected = new Complex(2*21.0,2*30.0); // also 42 + 60i
    boolean complexesEqual = actual.equals( expected ); // check
    boolean exp = true; // expectation
    // this next line is JUnit's test, with our own String annotation:
    assertEquals( "Complex equals", exp, complexesEqual ); 
  }

Commenting out tests...    One way to test your code one function at a time is to comment out all of the tests you don't want to compile or run yet. Java uses slash-star:

/*
and star-slash:
*/
in order to comment multiple lines at once. So, for example we could comment out the entirety of test_equals, above, as follows:
  /**
   * test of equals
   */
  /*
  public void test_equals() 
  {
    Complex actual = new Complex(42.0,60.0); // 42 + 60i
    Complex expected = new Complex(2*21.0,2*30.0); // also 42 + 60i
    boolean complexesEqual = actual.equals( expected ); // check
    boolean exp = true; // expectation
    // this next line is JUnit's test, with our own String annotation:
    assertEquals( "Complex equals", exp, complexesEqual ); 
  }
  */
Note that the function-header comment is an example of a multiple-line comment, too.

Dr. Java has support for JUnit testing: open both the Complex.java file and the ComplexTests.java file. Then hit the compile button (or F5) followed by the test button (or control-t). The testing tab will report on which tests passed and which did not.


Problem 2: Complex

Java has eight built-in (primitive) types -- the most common are such as int, char, boolean, and double. It has thousands of class types in its libraries. Yet one class type that Java does not have is Complex, representing complex numbers commonly used by mathematicians, scientists, engineers, and Math 11 students. This problem asks you to implement a complex number data structure (class). Complex.java. You should start with the two provided files:

Complex.java     and     ComplexTests.java

The Point class example we developed in class will be a good reference. After all, 2d points and Complex numbers are not so different... . The complete Point class is available here in Point.java and here in .txt format.

Complex number notes     Recall that a complex number is of the form a + bi where a and b are real numbers and i is an imaginary number. Addition of two complex numbers is defined by (a + bi) + (c + di) = (a+c) + (b+d)i. Similarly, to multiply two complex numbers, we simply compute (a + bi) * (c + di) = ac + adi + bci + bdii = (ac - bd) + (ad + bc) i since ii (i times i) is -1 by definition.

In your implementation, the numbers a and b in the complex number a + bi should be represented by doubles: these will be the two data members of your class. Please use the names real and imag (as already appear in the starter file) for these data members -- this will be far more readable/memorable than a and b!

Below are the public methods (functions) which your Complex class should contain. Be sure to use exactly these names and the specified kinds of arguments and return values. You may use any additional private methods of your own design, though such helper functions are not required.


Be sure to test out your Complex class with the unit tests provided before submitting your Complex.java file. You don't need to submit ComplexTests.java.


Problem 3: OpenList

This problem may be done individually or in a pair. If you work in a pair, follow pair programming practices throughout.

50 points

This problem is meant to introduce the implementation of data structures in Java. In addition, it is a chance to either introduce yourself (or review) what Java looks like and how java code is structured. If you haven't used Java before, you may need to review a bit about the language. In particular, there is a starting point for your OpenList.java and OpenListTests.java files on the top-level assignments page. Also, there are on-line references and tutorials available from the references page.

Write a java class named OpenList that implements an (open) linked list data structure. Since the goal of this problem is to transition from Racket/Scheme to Java, you will be implementing lists in the same way Racket does. In fact, this problem is, essentially, the first step in writing the Racket programming language in Java.

Open lists are characterized by not permitting modifications to existing data. As a result, users of OpenList can re-use lists many times without worrying about one part of their code changing a value that another part of their program might have needed. This means OpenLists do not permit side-effects: the changing of variables in-place.

Write your class in a file called OpenList.java. You should start with this the OpenList.java file. The main method of this OpenList.java contains a couple of informal tests (not JUnit tests). To begin, you might want to try compiling and running the file to start building intuition about how OpenLists work.

Tests    Just as with your Racket functions, you should test each of your methods as you write it. A starting set of JUnit tests is provided in OpenListTests.java In addition to running those tests, you should add at least one more test for each method you write in your OpenList class. You don't need to add a test for each of the {static, nonstatic} versions of each method, just one for each distinct name of a method you write.


Writing the OpenList class

There are three data members we will use in this OpenList. Here is how the class starts (this is in the provided OpenList.java file:


Notice that because the data member named first is of type Object, this OpenList implementation will be able to create lists of any Java Objects. Everything in Java is an Object except the types int, double, float, boolean, byte, char, and long. We will use only Strings and OpenLists for this assignment. The next assignment, on the other hand, will put your OpenList class to work handling more types.

In Java and other object-oriented languages, functions that are members of a class are often called methods.

So, in your OpenList class, implement the following methods. Note that both static and nonstatic versions of the methods are required in many cases. Feel free to implement one of the pair and then implement the other based on it.

Several methods are already provided in the starter file - please keep those!

Testing    Again, remember that you need at least one test for each of your functions in your OpenListTests.java file!

Submitting    Be sure to submit both your OpenList.java file and your OpenListTests.java tests to the submission site.