Harvey Mudd College
Computer Science 60
Assignment 3, Due Monday, September 26, 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 3 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).


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.

Testing your code!    Just as in Scheme, you should test each of your methods (functions) as you write it. To do this in Java, uncomment the lines in the provided Complex class's main method that test each of your methods. For this assignment, we will check visually that the answers are what they should be, so feel free to do the same.

It's much easier to make small syntactic errors in Java than in Scheme - there is simply more code where errors can appear! To help with this, the starter file provides lots of tests in a comment in main. As you implement your Java methods, uncomment the appropriate tests and try them out!

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 tests provided in main before submitting your Complex.java file.


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, and the additional file, MainFromCompletedOpenList.java, has tests that use more of the methods in their testing. Try them!

To begin, you might want to try compiling and running the file to start building intuition about how OpenLists work.


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. This is not typical -- it is simply to get used to the distinction between the two. 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, you will want to run at least the tests provided in the additional file above (you can copy-and-paste those to main).

Submitting    Be sure to submit your OpenList.java file to the submission site.