Click here to download the provided stub code that is to be used in the following programming assignment.

Your cousin Pierre has invited you to spend two weeks with his family in Paris, France. Before you leave, however, he warns you: "It's 35 degrees here!" Remembering that France measures temperatures in degrees Celsius, you frown... Does Pierre want you to pack your winter coat or your bathing suit? To figure that out, you need to convert 35 degrees Celsius to Fahrenheit.

Of course, we all know how to solve this problem. Simple algebra is sufficient, we may just have to look up the formula before we can apply it: F = (C / 5) * 9 + 32. In this case, C = 35, so the solution is

**F = (35 / 5) * 9 + 32 = 7 * 9 + 32 = 63 + 32 = 95**

35 degrees Celsius are equivalent to 95 degrees Fahrenheit. Pack the bathing suit.

By performing this little
calculation, we have solved our simple problem: We know how hot it is in

In this assignment, you'll be changing the problem slightly to see how design choices affect the complexity of the model. To ensure that your model and view are decoupled and can thus be changed independently from each other, you'll also write two user interfaces (views) for each model: A graphical user interface (GUI) and a text interface executing in the console. We have provided the code for the constant solution to get you started (click link to execute example or download source).

Example applet of the GUI:

Intuitively, the problem doesn't really change when we change the temperature in Celsius to 30 degrees. We should still be able to solve it using the same means; the general problem is about converting any temperature measured in the Celsius scale to a value in degrees Fahrenheit. We should be able to change the temperature without necessitating a change in the code. Since the temperature can vary, this solution is called a "variable solution".

**Exercise 1a: Identify the variant and the invariant of converting
a temperature from Celsius to Fahrenheit.**

**Exercise 1b: Draw a UML diagram of the model. It should consist of
a single static method.**

**Exercise 1c: Write a program that allows the user to convert any
temperature in Celsius to its corresponding value in Fahrenheit. You can start
by modifying the code for the constant solution, but remember to provide the
two user interfaces! Watch out for integer arithmetic being carried
out instead of floating point arithmetic.**

Example applet of the GUI (reverse engineering is prohibited!):

The solution you developed for Exercise 1 separated the variant from the invariants, and we can now convert any temperature in Celsius to the Fahrenheit scale. One shortcoming should be immediately apparent, though, especially when you look at your graphical user interface: You can only convert from Celsius to Fahrenheit, but not the other way! By using algebra and solving the above formula for C, we can derive a formula to convert degrees Fahrenheit to degrees Celsius:

C = 5/9 * (F - 32)

**Exercise 2a: Draw a UML diagram of a changed model that consists
of two static methods and allows conversion in both directions.**

**Exercise 2b: Write a program that implements this design. Again,
provide two user interfaces.**

Example applet of the GUI (reverse engineering is prohibited!):

The program developed for Exercise 2 allows the user to convert any temperature in either of the two temperature scales, Fahrenheit or Celsius, to the corresponding value in the other scale. Unfortunately, these aren't the only temperature scales in use: In many sciences, temperatures are measured in Kelvin, a scale whose origin is "absolute zero", the lowest possible temperature. Here is a formula to put Kelvin in relation to the Celsius scale:

K = C + 273.2

Your next program should support conversions in any direction between Celsius, Fahrenheit and Kelvin.

**Exercise 3a: If you follow the pattern from the first two solutions
of using one static method per possible conversion, how many methods will
you need for this model?**

**Exercise 3b: Draw the UML diagram of the changed model, with one
static method for each possible conversion.**

**Exercise 3c: Write a program that implements this design. Again,
provide two user interfaces.**

Example applet of the GUI (reverse engineering is prohibited!):

You are probably getting annoyed by now, so let's not write more code for a moment and just analyze our approach so far. Your program now supports conversion between three temperature scales in any direction, for any input value.

**Exercise 4a: Compare the model from Exercise 2 to the model from
Exercise 3. What is the ration between the number of methods in the models
of Exercise 2 and Exercise 3?**

There are even more temperature
scales. One that has fallen out of use in the 20^{th} century is the
Reaumur scale developed by the French naturalist Rene-Antoine Ferchault de Reaumur, with the freezing point of water at
its origin (just like Celsius) and the boiling point of water at 80.

**Exercise 4b: Assume we added full support for the Reaumur scale to
our program. Using our current approach of one method per possible conversion,
how many methods will you need?**

**Exercise 4c: Using your answers to Exercises 3a, 4a and 4b, derive
a formula - f - that allows you to calculate the number of methods,
given the number of temperature scales - n: f(n) =**

**Exercise 4d: What is the complexity (i.e. the number of methods)
of this approach, expressed in "big O" notation?**

As you can tell from
your answer to Exercise 4c, the number of methods with our current approach
grows very quickly. Even if you add just one more scale, you will have to
write many more methods - two for each of the scales that were already there.
If you had 100 scales and added a 101^{st}, you would have to write
200 additional methods! There has to be a better way.

Let's reconsider what we are doing: We are converting temperatures, and even though we can express the temperature in different scales, resulting in different values, the meaning of that value is still the same: 100 degrees Celsius is the same as 212 degrees Fahrenheit, which is the same as 373.2 Kelvin. That means that we do not have to be able to convert from Fahrenheit to Kelvin directly, we can convert from Fahrenheit to Celsius first and then convert from Celsius to Kelvin! As long as we can somehow, using the conversion formulas we have, get from each scale to each other scale, our program can still perform conversions between arbitrary scales.

**Exercise 5a: Using this new approach, what is the minimum number
of methods we need to convert between three scales? Four scales? n
scales?**

**Exercise 5b: What is the complexity (i.e. the number of methods)
of this new approach, expressed in "big O" notation?**

If we draw a directed graph (a "web" with "dots" and "arrows" between the dots) of this minimal arrangement with the temperature scales as nodes ("dots") and the conversion methods as edges ("arrows"), the graph is star-shaped: There is one node in the middle, all other nodes are arranged around it, and there is an arrow going from the central node to each of the outer nodes, and an arrow going from each of the outer nodes back to the central node.

**Exercise 5c: Draw a UML diagram of the model using the new approach.
The model should have the minimum number of methods to still perform arbitrary
conversions between Celsius, Fahrenheit, and Kelvin.**

**Exercise 5d: Write a program that implements this design. Again,
provide two user interfaces; they should look the same as in the program for
Exercise 3c.**

Our new approach lets
us add new temperature scales by adding only a small, constant number of methods.
We say that an "epsilon" (i.e. small) change in the specification will only
cause a "delta" (i.e. proportional or manageable) change in the code. Theoretically,
we can quite easily add an arbitrary number of scales. Can we come up with
a design that allows us to do that without touching existing code *at all*?
To do that, we need to separate variants from invariants again.

Before doing that, we
are going to make one change to our graph of temperature scales: Let's put
Celsius both in the center of the graph *and* on the outside. Even though
the scale in the center is a Celsius scale, from now on we will call it "base
scale". It may look like we just made our life more complicated, since we
now have four nodes instead of just three, but this is actually a simplification:
Before Celsius was treated differently because it was in the center; now Celsius
is on the outside as well, and all scales are treated the same. This is what
the graph looks like now:

To be more precise, let's give our conversion functions names. These are the six conversion functions our program will be able to handle:

CF(x) |
- convert x from Celsius to Fahrenheit |

CK(x) |
- convert x from Celsius to Kelvin |

FC(x) |
- convert x from Fahrenheit to Celsius |

FK(x) |
- convert x from Fahrenheit to Kelvin |

KC(x) |
- convert x from Kelvin to Celsius |

KF(x) |
- convert x from Kelvin to Fahrenheit |

And these are the six conversion functions we actually write:

CB(x) |
- convert x from Celsius to
base scale |

BC(x) |
- convert x from base scale
to Celsius |

FB(x) |
- convert x from Fahrenheit
to base scale |

BF(x) |
- convert x from base scale
to Fahrenheit |

KB(x) |
- convert x from Kelvin to
base scale |

BK(x) |
- convert x from base scale
to Kelvin |

Since the base scale is identical to the Celsius scale, the functions *CB*
and *BC* are just the identity function: *CB(x) = BC(x) = x*. We
can now express the six functions in the first group using the six functions
in the second group:

CF(x) = BF( CB(x) ) |
CK(x) = BK( CB(x) ) |

FC(x) = BC( FB(x) ) |
FK(x) = BK( FB(x) ) |

KC(x) = BC( KB(x) ) |
KF(x) = BF( KB(x) ) |

To convert from one temperature scale to another, we first apply the input value to the function that takes us to the base scale, and then apply the result to a second function, the one that takes us from the base scale to the target scale:

- To convert from Fahrenheit to Kelvin, we first apply
*FB*to*x*; then we apply*BK*to the result of the first application. - To convert from Celsius to Fahrenheit, we first apply
*CB*to*x*; then we apply*BF*to the result of the first application. - To convert from Fahrenheit to Celsius, we first apply
*FB*to*x*; then we apply*BC*to the result of the first application.

Or abstractly:

- To convert from a source scale "s" to a target scale "t", we first apply
*sB*to*x*; then we apply*Bt*to the result of the first application.

**Exercise 6a: What differs and what always remains the same when performing
conversions this way? Identify the variants and the invariants. Remember that
we can pass functions as data in an object-oriented language like Java.**

If we want to add Reaumur as a fourth scale to our model, all we need to do is provide two additional functions:

RB(x) |
- convert x from Reaumur to base scale |

BR(x) |
- convert x from base scale to Reaumur |

These two functions, together with the six others, give us the ability to handle the six conversions from and to Reaumur:

CR(x) = BR( CB(x) ) |
FR(x) = BR( FB(x) ) |
KR(x) = BR( KB(x) ) |

RC(x) = BC( RB(x) ) |
RF(x) = BF( RB(x) ) |
RK(x) = BK( RB(x) ) |

**Exercise 6b: What three pieces of data are necessary to define a
temperature scale in our model? Hint: These are the invariants you identified
earlier.**

In functional languages,
a function that is passed as data so it can later be applied is called a lambda;
in object-oriented languages, it is also called the command design pattern.
For this exercise, you can implement the *ILambda* interface for this purpose.

public abstract R apply(P param);

}

It is interesting to
note that the two functions that describe a temperature scale (e.g. *RB*
and *BR* for the Reaumur scale) are not just any functions: They are
inverses of each other, i.e. *BR( RB(x) ) = x*. A function that has an
inverse is called a bijection. We have therefore
provided an *IBijection* interface that extends *ILambda* and is able to provide its own inverse,
which is also an *IBijection*.

public abstract IBijection<P, R> getInverse();

}

Since an *IBijection* can provide its inverse, one *IBijection* instance can be used to represent
both the conversion function to the base scale and the function from the base
scale back. The class that represents an abstract temperature scale, AUnit, therefore needs only two fields
for the variants, and one method expressing the invariant.

Just like in physics, you need to keep track of both a number and a unit (i.e. the temperature scale). To make sure the correct temperature scale remains associated with the number, it is a good idea to introduce a class to represent this number-unit pair. We have provided the Measurement class for this purpose.

**Exercise 6c: Using the IBijection interface and the AUnit
and Measurement classes, draw a UML diagram of the model with the Celsius,
Fahrenheit, Kelvin, and Reaumur temperature scales.**

**Exercise 6d: Write a program that implements this design. Again,
provide two user interfaces. We have provided some stub
code to help you get started.**

Press the button on the applet to see an example of the GUI (reverse engineering
is prohibited!):

Note: Adding new scales on-the-fly using reflection may not work when using the applet. For a demonstration of reflection, please download the fullNS.jar file and run the application using the following command line:

`java -cp fullNS.jar controller.TempCalcApp`

Good luck!