Harvey Mudd College
Computer Science 42
Assignment 2

Due Wednesday, September 26, by 11:59pm

Unicalc API

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.

Design, commenting, testing and formatting

Design, formatting and testing will account for roughly a quarter of the credit for each function or program you complete.

The graders will keep an eye out for your overall design with these guidelines in mind.

Testing

Again, we will provide several tests in the problem descriptions. These example are not complete, however -- designing and using your own tests is crucial to testing all of the cases your code needs to handle.

Function names

Be sure that you name your functions as specified in each problem -- this helps enormously with the grading! Helper functions may have any names appropriate for their operation.

The Problems

Start by downloading the support files for this problem:

·       unicalc-db.rkt  The database file.

This is the first part of a 3-part assignment. Unicalc is a calculator that includes physical and other units, rather than just numbers.  Units are important to computation because they eliminate an element sometimes left to human interpretation. In the area of engineering, failure to interpret numbers without their units specified has been known to lead to failure of space missions for example. In the wikipedia article about NASA’s Mars Climate Orbiter, it is stated:

“The Mars Climate Orbiter was intended to enter orbit at an altitude of 140–150
km (460,000-500,000 ft.) above Mars. However, a navigation error caused the
spacecraft to reach as low as 57 km (190,000 ft.). The spacecraft was destroyed
by atmospheric stresses and friction at this low altitude. The navigation error
arose because a NASA subcontractor (Lockheed Martin) used Imperial units
(pound-seconds) instead of the metric system.”

A little closer to home, you will have to do many unit conversions and operations in your other classes here at HMC.  It is our hope that this calculator will prove useful in those classes.

Data Representation

At the heart of the Unicalc back-end is our choice of data representation.  We will be working with numerical quantities, including units.  Examples of these quantities in conventional notation, would be as follows, where ^ denotes raising to a power.


420 seconds
4.2 meters / second^2
42.42  mile / hour


We need some way to represent this data in Racket.  In particular, we need to create a data abstraction so that whatever representation we choose, it would be easy to change in the future if we so desired.

This week, we will represent our data as certain lists structures, which we call Quantities.  The Quantity data abstraction consists of a numeric multiplier, a flat list of symbols (not strings) as the numerator, and a flat list of symbols as a denominator.


;; Data abstraction for a Quantity
; Function: make-quantity
; Arguments:
;    multi: a number
;    numer: a flat list of symbols representing the units in the numerator
;    denom: a flat list of symbols representing the units in the denominator
; Result:
;    the representation of a Quantity


(define (make-quantity multi numer denom)
                 (list multi numer denom))

; Accessor functions for a Quantity

(define get-multi first)

(define get-numer second)

(define get-denom third)



Using this representation, the above examples would look like:

‘(420 (second) ())
‘(4.20 (meters) (second second))
‘(42.42  (mile) (hour))



NOTE: Although
make-quantity, get-multi, get-numer and get-denom simply rename the functions first, second and third, when you use quantities in this assignment, you should always use the named functions and never use first, second, third directly for this purpose, even though they do the exact same thing currently. This is so that if you need to change the meaning of any of these functions, as you may in the future, you do not have to worry about confusing them other uses of the basic built-in functions.

Symbols, rather than strings, are what are contained in the flat lists inside a quantity. You will want to be able to compare symbols alphabetical. You can create your own symbol<? Function, using the information in http://www.cs.hmc.edu/courses/2012/fall/cs42/symbolsAndStrings.rkt

 

Database

On the main course page, and above on this page, you will find unicalc-db.rkt. This is a Racket source file for the Unicalc database. The database is, in effect, a set of equations defining single symbols, called units, in terms of Quantities. As a Racket structure, it is an association list to which variable unicalc-db will be bound. You will want to download this file into the same directory as your code.

The Unicalc API

The API is a set of functions that you will construct, as described below, along with other helper functions. To describe our functions, we need a couple more definitions.

Defined Units: A symbol that is defined in the database (as the first element of an element of the association list) is called a defined unit.

Basic Units: A symbol that is not a defined unit is called a basic unit. So basic units have that property due to the omission of definitions for them in the database.

For unicalc-db as currently constituted, examples of defined units are: mile, day, pound. Examples of basic units are: kg, second, meter, and most other SI units.

Defined units can be expanded into equivalent Quantities consisting of only basic units in one or more steps, based on definitions in the database. (You can assume for now that there are no circular definitions in the database.)

Basic units can be trivially converted into quantities by making them the only element in the numerator list, accompanied by a multiplier of 1 and an empty denominator list.


Normalized Quantities: A Quantity is called normalized provided that the following two conditions are true:

·       The numerator and denominator lists consist of only basic units. (Either could be empty.)

·       The numerator and denominator lists are sorted in ascending alphabetic order.

·       The numerator and denominator have no units in common.


Requirements

Here are the functions you need to provide in the API. We don’t require the user to provide normalized Quantities, but it helps make processing more efficient if internally use only normalized quantities. Note that divide effectively achieves conversion from one kind of normalized Quantity to another. Below, Q1 and Q2 always represent Quantities.

Function Call Form

Meaning

(normalize-unit Unit)

Returns a normalized Quantity corresponding to 1 in the given unit (where this Unit is a single unit such as 'mile or 'kg or 'second)

(normalize Q1)

Returns a normalized Quantity from any Quantity Q1.

(multiply Q1 Q2)

Returns the normalized result of multiplying normalized quantity Q1 by Q2.

(divide Q1 Q2)

Returns the normalized result of dividing normalized quantity Q1 by Q2.

(add Q1 Q2)

Returns the result of adding normalized Q1 to normalized Q2, provided the quantities are interconvertible. The result should be in units of Q1.
Returns a list
(‘error (‘add Q1 Q2)) value if not. 

(subtract Q1 Q2)

Returns the result of subtracting normalized Q2 from normalized Q1, provided the quantities are interconvertible. The result should be in units of Q1.
Returns a list (‘error (‘subtract Q1 Q2)) if not.

(power Q1 N)

Returns the result of raising Q1 to the integer power N returning a normalized Quantity.  If N is not an integer, returns a list (‘error (‘power Q1 N)) if not. You should allow for negative powers.


Note that the database,
unicalc-db, is global and is imported to these functions, rather than being passed as an argument.

 

You will need other functions to make all of this work, but those functions will not be advertised to the user. Here are a few suggested utilities. These will also provide some practice with simpler functions.

 

Function Call Form

Meaning

(make-normalized-quantity multiplier numerator denominator)

Make a normalized Quantity from a multiplier, a list of units for the numerator, and a list of units for the denominator. I will provide this definition as an example.

(make-unit-quantity Unit)

Make a normalized Quantity from a singe unit.

(make-numeric-quantity Number)

Make a normalized Quantity from a singe number.

(merge L M)

Merge two lists of symbols that are already sorted into a single sorted list.

(cancel L M)

Given two sorted lists of symbols, for each instance of a symbol in L that occurs in M, remove an instance from L.

(normalize-all L)

Returns the normalized equivalent of the product of a list of units.

one

Not a function, but a constant that represent the number 1 as a normalized Quantity.



Testing

Although we have provided test cases, you should also include at least one more test case for each of the functions specified above.  Include these test cases in your a02.rkt source file.  We will also test your code on additional cases not provided.

To reduce amount of typing and space that is occupied by test cases, I provide two convenience functions. These are to simplify the user interface only. The cannot be used to solve the main problems, as this would create circularity.

Function Call Form

Meaning

(mq S-expression)

Make a Quantity from an S-expression, such as ‘(42.24 (meter)(second second)), by calling make-quantity.

(mnq S-expression)

Make a normalized Quantity from an S-expression, such as ‘(42.24 (meter)(second second)), by calling make-normalized-quantity.

 


 

Extra Credit: Adding Apples to Oranges [worth up to 75% of the main problem]

Rather than throwing an error in the case of addition and subtraction of non-interconvertible quantities, we could return a Super-Quantity representing the sum or difference. This would require a special data representation distinguishing a Super-Quantity from an ordinary Quantity. The content of a Super-Quantity would be a list of two or more Quantities. Then we could represent ideas such as:

            add(3 apples, 2 bananas) gives “3 apples and 2 bananas”

 

While returning such a Super-Quantity for adding and subtracting pure Quantities is not difficult, we’d have to have closure, meaning that add and subtract could take Super-Quantities as arguments. Moreover, we’d have to extend multiply, divide, and power to accept Super-Quantities as well. This will require some design.

 

It is required that Super-Quantities always have an extra level of normalization that groups interconvertible summands. For example, adding “3 apples and 2 bananas” to “4 oranges and 5 bananas” should give “3 apples, 7 bananas, and 4 oranges”.

 

If you decide to try this problem, include test cases to show that your solution does have closure.