CIT 591 Assignment 9: Negadecimal Calculator
Fall 2014, David Matuszek

Purposes of this assignment

Note: This is probably the most abstract (and least practical) assignment you will get this semester.

General idea of the assignment

Your assignment is to write a "calculator" for negadecimalexternal link integers. It will be able to do addition, subtraction, multiplication, and integer division, as well as negation and modulus (remainder). It will also have a one-number "display,", and it will be able to convert between decimal and negadecimal.

Negadecimal is a place notation like decimal, only instead of powers of 10, the notation uses powers of -10: 1, -10, 100, -1000, 10000, -100000, etc. For example,

The number:     7   1   0   2   5
In decimal, means:     7*10000   1*1000   0*100   2*10   5*1
In negadecimal, means:     7*10000   1*-1000   0*100   2*-10   5*1

So, while 71025 is our usual decimal notation for 7102510 , the negadecimal number 71025-10 , translated to decimal, would be 68985.

An interesting characteristic of negadecimal is that numbers with an even number of digits are negative, while numbers with an odd number of digits are nonnegative (zero or positive).

It is easy to translate from negadecimal to decimal:

5 times 1 is 5
2 times -10 is -20
0 times 100 is 0
1 times -1000 is -1000
7 times 10000 is 70000

5 - 20 + 0 - 1000 + 70000 = 68985

However, converting to negadecimal is more complicated. Fortunately, the Wikipedia article linked to above gives a C++ method for converting to negaternary, and this method can be easily adapted to one that converts to negadecimal. (Important: Don't try to use the Python version; the % operator gives different results for negative numbers.)

The user will use your program by typing in negadecimal expressions that modify the display. For example, if the display holds 500 and the user types in +692 , the calculator will compute 500+692 and display as 19192 as a result. Details of what the user may type are given below.

You should do unit testing of all your methods that don't do I/O, and we will also unit test your program. As you know, that means that our tests must be able to call your methods, so you should be extremely careful to write functions exactly as specified below.

Classes

NegadecimalNumber

Each instance (object) of this class will represent one negadecimal number. That negadecimal number will be retained in an instance variable of type int.

The NegadecimalNumber class will have two constructors:

public NegadecimalNumber(String s)
This constructor expects a string representation of a negadecimal number, which it converts to a (decimal) int and stores in an instance variable. However, if the string does not consist entirely of digits, or is an empty string, an IllegalArgumentException should be thrown.
public NegadecimalNumber(int n)
Saves the integer (decimal) parameter in an instance variable.

In addition, the NegadecimalNumber class has a number of methods for maniuplating negadecimal numbers, and for combining them with other negadecimal numbers:

public NegadecimalNumber add(NegadecimalNumber ndn)
Returns a new negadecimal number which is the result of adding ndn to "this" negadecimal number.
public NegadecimalNumber subtract(NegadecimalNumber ndn)
Returns a new negadecimal number which is the result of subtracting ndn from "this" negadecimal number.
public NegadecimalNumber multiply(NegadecimalNumber ndn)
Returns a new negadecimal number which is the result of multiplying ndn and "this" negadecimal number.
public NegadecimalNumber divide(NegadecimalNumber ndn)
Returns a new negadecimal number which is the result of dividing "this" negadecimal number by ndn. Throws an ArithmeticException if ndn is zero.
public NegadecimalNumber remainder(NegadecimalNumber ndn)
Returns a new negadecimal number which is the remainder after dividing "this" negadecimal number by ndn. Throws an ArithmeticException if ndn is zero.
public NegadecimalNumber negate()
Returns a new negadecimal number which which, when added to negadecimal, would give zero.
public int decimalValue()
Returns the decimal equivalent of this negadecimal number.
public boolean equals(NegadecimalNumber ndn)
Returns true if this number and ndn have the same value. Necessary for unit testing.
public String toString()
Returns the string representation of this negadecimal number.

NegadecimalCalculator

This class contains a main method. When executed, this method acts as a simple calculator, using the negadecimal number system. The calculator's "display" shows the (negadecimal) number currently in the calculator (0 when the program first starts up. Subsequent operations use this number and, in the case of a binary operation, the number entered by the user, to compute a new number to display.

In the descriptions below, the word integer will refer to an ordinary int value. The term negadecimal number (ndn for short) will mean a string composed of decimal digits only.

The user may type in any of the following expressions. After entering an expression, the calculator performs the requested operation and displays the result. If the user enters an illegal number (a blank line, or something not composed entirely of digits), the word Error will be displayed instead.

ndn Replaces the number currently in the display with this new number ndn.
+ ndn Adds ndn to the current display.
- ndn Subtracts ndn from the current display.
* ndn Multiplies the current display by ndn.
/ ndn Divides the current display by ndn. Displays Error if ndn is zero.
% ndn Gives the remainder when dividing the display by ndn.
~ Computes the negative the display, that is, zero minus the number in the display.
? Shows the decimal value of the displayed negadecimal number, then redisplays the negadecimal number. This is a way of "peeking" at the decimal value of a number without modifying the current value in the calculator.
decimal integer Replaces the number currently in display with the negadecimal equivalent of the decimal number entered by the user. This is the only expression for which the number entered by the user is a decimal value.
clear Clears any errors and replaces whatever is in the display with 0.
quit Quits the program.

Your "calculator" will remember the last number computed (initially zero, when you start the program). In each of the above expressions,

The Negadecimal calculator should have (at least) the following methods:

public String evaluate(String s )
Takes a string that may have been entered by the user (or may have come from our test methods), and
  1. Finds the various parts of the string (for example, first operand, operator, second operand).
  2. Tries to parse the input, that is, recognize what kind of command it is. If the input does not appear to be a legal command, return an error message (possibly just "Error").
  3. Calls one of the above methods to perform the arithmetic.
  4. Returns the result as a string.

You may find it useful to write "helper functions" for each of steps 1, 2, and 3, otherwise you may end up with a very long function.
public void REPL()
This method implements a basic Read-Evaluate-Print-Loop. It should:
This should be the only function that does any input/output.
 
public static void main(String[] args)
This is the method that begins the program. All it should do is create an instance of NegadecimalCalculator and call its REPL method.

Example (I am using "Compute:" as a prompt; all numbers except the response to ? are negadecimal numbers):

Compute: 195
195
Compute: ?
195 (decimal 15)
Compute: +10
5
Compute: - 20
185
Compute: hello
Error
Compute: c
0
Compute: quit
Done.

Programming hints

JUnit testing

Let Eclipse do most of the work for you!

After you have stubs for all your methods in a class, make sure the class is selected, then go to File → New → JUnit Test Case. Make sure "New JUnit 4 test" and "setUp()" are selected. Don't click Finish! Click Next> and select the methods you want unit tests for. Then you can click Finish.

All of the methods are set up to call fail("Not yet implemented"). Replace these calls with more meaningful calls. The most commonly used methods are:

For method calls that you expect to throw an assertion, you need to modify the @Test annotation to read
@Test(expected=TypeOfException.class)
and inside the test method, you don't need to call any "assert" methods, just call the method you want to throw an exception.

Due date

Turn your assignment in to Canvas before 6am Friday, November 7.