The Tiny RPN Calculator: Version 1.02 Overview

Extending the Calculator

This draft is an extensive revision of Version 1.01. The chief objective is to enable the user to write and invoke not just one program but as many as he or she wishes. That in turn means the run command is now obsolete, and programs must be invoked by their individual names. To implement these changes, the Program class was revised to include another constructor, fields and methods to assign names to programs and access them by name, and the like. At the same time, since programs are no longer monitoring their own operations, we need another class to do that. Hence the ProgramManager class is introduced, and an instance of ProgramManager, called pgmManager, is made a member of the Calculator class.

The ProgramManager class contains a vector of type Program to enable storage and retrieval of a multitude of programs. ... (More detail to follow soon...)

The four classes we now have on hand—Program, ProgramManager, Stack, and Calculator—are the only ones we'll use in Versions 1.02 through Version 1.05. For Version 1.06, we will incorporate one more class to extend the calculator's functionality. Otherwise, we will add fields and methods as necessary to the classes we now have on hand (except for the Stack class, which is functionally complete: it doesn't know what's happening in the rest of the program, and it doesn't care, nor should it care).

Version 1.02 also incorporates several revisions to the Calculator class. Chief among them is conversion of the DeployMathFunction() method from a lengthy if-else statement to an extended switch statement. The various math functions are no longer accessed by name (not immediately, anyhow), but rather by number. The new member method MathFunctionIndex() is introduced to assign to each math function an index number, and communicate that number to the method DeployMathFunction(). It must be pointed out that using a switch statement in lieu of a lengthy if-else statement is not necessary from a purely functional standpoint, nor would it ever be. However, it does have certain advantages irrespective of the additional modest overhead imposed. One, it makes the code a lot more legible. Two, it provides a handy way to classify the math functions according to whether they take one or two arguments. That classification will be indispensable as we try to extend the calculator's capabilities in Version 1.06.

In the process of converting to the switch statement we effected three other changes: (1) We eliminated the hard-coding of constants (except for pi and e, which are not there for show!). Why? Because now we can program as many constants as we need, particularly the physical constants, and name and use them accordingly; (2) We split off the default condition (i.e., "The entry is neither a math function nor a program name") from the switch statement and made a separate method for it, called Calculator::ReportUnsupportedEntry() (in that vein we also made the trapping of program names more explicit by including the empty function Calculator::TrapProgramName()); (3) Because reporting of unsupported entries is no longer the job of Calculator::DeployMathFunction(), the Calculator object needs to test for the presence of math function names. So a very simple function called EntryIsMathFunctionName() is written in conjunction with MathFunctionIndex(). If the latter returns an index greater than or equal to 0, the former returns the Boolean value true; if MathFunctionIndex returns -1 instead, there is no math function name present, and EntryIsMathFunctionName() returns false.

Writing Multiple Programs

It's time to see how all these revisions translate into concrete results. We begin by writing a program that looks vaguely familiar: it's another take on the Golden Ratio. This time, when we type the pgm command, we're prompted to name our new program. We'll name it gold1, and write it as follows:

pgm
Enter a name for the program: gold1
Enter a sequence of program instructions; terminate by entering "end": 1 5 sqrt + 2 / ? end
   Program "gold1" created.

Now we'll compose a second program called diff, which copies the output of gold1, takes its reciprocal, then subtracts the reciprocal from the original:

pgm
Enter a name for the program: diff
Enter a sequence of program instructions; terminate by entering "end": dp r - ? end
   Program "diff" created.

To demonstrate that not only can the Tiny Calculator now run more than one program, but also chain programs together, we will run gold1 and diff on the same command line, by simply entering their names separated by a space:

gold1 diff
   1.61803398875
   1

It appears that invoking multiple programs works as expected. However, it might be nice to modify diff by having it output the reciprocal of gold1's output before taking and outputting the difference. In Version 1.01, if we wanted to overwrite the lone program available, the calculator would let us do it without warning. Since we've engineered the capacity for multiple programs in Version 1.02, and created a ProgramManager class for that purpose, we've also taken a few steps to make this version a bit more robust and user-friendly:

pgm
Enter a name for the program: diff
   A program of this name already exists. Do you want to overwrite it? (y/n) y
Enter a sequence of program instructions; terminate by entering "end": dp r ? - ? end
   Program "diff" created.

Observe that Version 1.02 contains a provision not present in Version 1.01: this version prompts us with a query about whether to overwrite an existing program. As more sophisticated versions of the calculator are developed, we're going to build in more of this type of machinery, in hopes of preventing users from making mistakes that might prove costly. Now let's run gold1 and the new diff in succession as before:

gold1 diff
   1.61803398875
   0.61803398875
   1

Lastly, we'll draft another take on the Golden Ratio, called gold2, that is much like gold1 except that it takes the difference of one and the square root of five rather than their sum. Then we'll invoke gold2 in conjunction with diff as before:

pgm
Enter a name for the program: gold2
Enter a sequence of program instructions; terminate by entering "end": 1 5 sqrt - 2 / ? end
   Program "gold2" created.
gold2 diff
   -0.61803398875
   -1.61803398875
   1

An interesting set of results, to be sure. But again, programs like gold1 and gold2 will lose their luster very quickly, because they're of the same sort as the "Hello, world!" program of so many introductory texts on computer programming. They take no inputs and always output the same result. As you'll see when you come to Exercises 2 and 3 below, we will generalize these formulas in a way that makes programming them a good deal more interesting, and more edifying as well.

Looking Ahead

We've come a long way with these revisions, but in two important respects Version 1.02 still leaves a great deal to be desired. One is that while we can now chain program commands together in a single sequence of instructions, we cannot call programs from other programs. That would be a very useful thing for a programmable calculator to do; in fact, it is absolutely essential that a programmable calculator be able to nest programs within other programs. Second—and quite importantly— this draft doesn't contain sufficient provisions for validating inputs. What if the user creates a program called "sin", or "exp"? If she does, the program instructions she chooses for it will temporarily override the sin and exp math functions. That may be harmless in some circumstances, but in others it is bound to produce peculiar and unexpected results. (Similarly, and perhaps worse, if she names programs "=", or "49"!) It would be much better to prevent the user from assigning to programs names that are either numeric literals, or variable names, or math operators, or names reserved for the calculator's own commands. Version 1.03 is designed to address both of these critical issues.

Exercises

1) Write a program called mycosh that takes the value at the top of the stack and computes and outputs its hypercosine "the hard way." Write another program called mysinh that takes the value at the top of the stack and computes and outputs its hypersine, again "the hard way." Write a third program called diffsq that utilizes these results to verify the hyperbolic identity cosh2 x - sinh2 x = 1 . Exercise your faculties with RPN by using stack functions in lieu of any variable assignment. Test your programs with a variety of inputs.

2) Consider the following function:  y1 = (x + (x2 + 4)½) / 2 . This function effectively expresses the following relation: "For every real number x there exists a real number y1 such that y1 minus the reciprocal of y1 equals x." Write a program called y1 that evaluates this function given an input x. Write a second program called rec that verifies the reciprocal relation expressed above. Execute both programs on the same command line, and use a variety of inputs to test them.

3) If you are a curious sort, perhaps you have examined the relation  y1 - 1 / y1 = x . If you have attempted to solve for y1 in terms of x, you may have discovered that y1 is just one of two real roots of a quadratic equation. That root is the function expressed in Exercise 2. The other root may be expressed as  y2 = (x - (x2 + 4)½) / 2 . It turns out that the reciprocal relation holds for this root as well: namely, for every real number x there exists another real number y2 such that y2 minus the reciprocal of y2 equals x. Write the program y2, and using rec, confirm that the reciprocal relation holds for y2 just as it does for y1.

4) Write a program called raw that computes the raw displacement of an engine regardless of the units in which it's measured. The user will enter bore, stroke, and number of cylinders in that order. Use stack functions to perform the computation, then store the "unitless" raw displacement in the variable D. Then write two more programs, one called ede ("Engine Displacement—English Units"). This program should take the result yielded by raw, and produce two outputs: one in cubic inches, rounded to the nearest thousandth of a cubic inch; the second in cubic inches, rounded to the nearest cubic inch. The other program is called edm ("Engine Displacement—Metric Units"), and should format raw displacement as follows: (a) cubic centimeters, rounded to the nearest cubic millimeter; (b) cubic centimeters, rounded to the nearest cubic centimeter; (c) liters, rounded to the nearest tenth of a liter. Use as your examples the Chevrolet 283 small-block engine of 1957-67 (bore: 3.875 in.; stroke: 3.00 in.; 8 cylinders); the Volkswagen New Beetle (bore: 82.5 mm.; stroke: 92.8 mm.; 5 cylinders); the Lotus-Ford Twin Cam engine of 1962 (bore: 3.25 in.; stroke: 2.90 in.; 4 cylinders); and the original Lamborghini Murciélago (bore: 87.0 mm.; stroke: 86.8 mm.; 12 cylinders).

Solutions to Exercises

1) Here's a way to execute the solution without resorting to variable assignments. Observe that on the command line we have mixed our new, custom-made program commands with pre-existing commands built into the calculator, such as dp and sw. If you think of your programs as being compounds of commands, you'll increasingly come to view them as custom-built extensions of the commands natively featured in the calculator.

pgm
Enter a name for the program: mycosh
Enter a sequence of program instructions; terminate by entering "end": dp exp sw chs exp + 2 / ? end
   Program "mycosh" created.
pgm
Enter a name for the program: mysinh
Enter a sequence of program instructions; terminate by entering "end": dp exp sw chs exp - 2 / ? end
   Program "mysinh" created.
pgm
Enter a name for the program: diffsq
Enter a sequence of program instructions; terminate by entering "end": sq sw sq ? sw ? - ? end
   Program "diffsq" created.
1.5 dp mycosh sw mysinh diffsq
   2.35240961524
   2.12927945509
   5.53383099789
   4.53383099789
   1
e dp mycosh sw mysinh diffsq
   7.61012513866
   7.54413710282
   57.9140046261
   56.9140046261
   1
pi dp mycosh sw mysinh diffsq
   11.5919532755
   11.5487393573
   134.373380742
   133.373380742
   1
4.28571428 dp mycosh sw mysinh diffsq
   36.3340937894
   36.3203300026
   1320.1663715
   1319.1663715
   1

2) The solution is as follows:

pgm
Enter a name for the program: y1
Enter a sequence of program instructions; terminate by entering "end": dp sq 4 + sqrt + 2 / ? end
   Program "y1" created.
pgm
Enter a name for the program: rec
Enter a sequence of program instructions; terminate by entering "end": dp r ? - ? end
   Program "rec" created.
1 y1 rec
   1.61803398875
   0.61803398875
   1
2 y1 rec
   2.41421356237
   0.414213562373
   2
70 9 / y1 rec
   7.90429133324
   0.126513555465
   7.77777777778
pi 1000 * y1 rec
   3141.5929719
   0.000318309853932
   3141.59265359

3) The solution is as follows:

pgm
Enter a name for the program: y2
Enter a sequence of program instructions; terminate by entering "end": dp sq 4 + sqrt - 2 / ? end
   Program "y2" created.
1 y2 rec
   -0.61803398875
   -1.61803398875
   1
2 y2 rec
   -0.414213562373
   -2.41421356237
   2
e y2 rec
   -0.328242866874
   -3.04652469533
   2.71828182846
40 7 / y2 rec
   -0.169945728631
   -5.88423144292
   5.71428571429

4) The solution is as follows:

pgm
Enter a name for the program: raw
Enter a sequence of program instructions; terminate by entering "end": * sw 2 / sq pi * * D = end
   Program "raw" created.
pgm
Enter a name for the program: ede
Enter a sequence of program instructions; terminate by entering "end": D 1000 * round 1000 / ? D round ? end
   Program "ede" created.
pgm
Enter a name for the program: edm
Enter a sequence of program instructions; terminate by entering "end": 
D round 1000 / ? D 1000 / round ? D 100000 / round 10 / ? end
   Program "edm" created.
3.875 3.00 8 raw ede
   283.038
   283
82.5 92.8 5 raw edm
   2480.366
   2480
   2.5
3.25 2.90 4 raw ede
   96.231
   96
87.0 86.8 12 raw edm
   6191.977
   6192
   6.2