Calc - Java Calculator for cell-phones and MIDP devices

Programming examples

In the program listings, the most basic commands are written as just the command name, such as "ENTER", "*", and numbers such as "0.5". Commands that are found deeper down in the menu hierarchy will be written prefixed with some parts of the menu choices needed to get to the command. This is done to remind the user how to find the command, it is not meant as a complete description of how to find the command. For example, if the program listing contains "stack/RCL st# 1", what this actually means is "main/special/stack/more/RCL st#/<0-3>/<1>".

Programming hint: If a program needs parameters pushed onto the stack before running, it is useful to push some dummy arguments on the stack before starting to record the program, to be able to follow the programming with actual data.

Drawing a function

Many people have requested a more thorough explanation of how to do such a simple thing as drawing a function. To recap the long and perhaps tedious documentation, a funtion is a program that uses the lowest element on the stack (x) as input, calculates the function value based on this input, and leaves the resulting function value in the lowest element on the stack as output.

We will proceed to program and draw two different programs of different complexity. The first program represents one of the simplest functions possible:

    f(x) = x

Let's see how we can make a program to calculate this function. When the program is called by the graph drawing operation, the input value x will be present as the lowest element on the stack. Since the input value of this particular function equals the output value, we don't have to do anything to it to it to calculate the function value. Also, since the output value of the function should be located in the lowest stack element when the program finishes, the program does not have to do anything either. Therefore, the operations you need to execute to program this function are as follows:
prog/new "f1"
prog/finish

To draw the function, we first need the boundaries of the draw area pushed on the stack. We will use the area -10<x<10 and -10<y<10. After entering the limits, we may execute the draw operation:
-10
10
-10
10
prog/draw/y=f(x)/f1

This looks rather boring. However, the function is not completely useless. If we use other drawing modes (with the same limits as above), we get some nice results:
prog/draw/r=f(θ)/f1
prog/draw/z=f(z)/f1

Now for a more complex example. The function we want to draw now is the following:

    f(x) = 2x² - 3x - 1

One of the first problems we have to deal with is that we need 'x' twice to calculate this function, and there is only one 'x' on the stack when the program starts. We could use "mem/STO" to store the x in a memory location, and "mem/RCL" to get it back whenever we need it, but a more common method is just to dupliacte the number on the stack, using ENTER. One possible solution to program the function follows. Before programming, clear the stack and enter a dummy input value on the stack, e.g. '1', to be able to follow the calculation:
stack/clear              ; clear the stack
1                        ; enter dummy input value (be wild: try '2' instead)
                         ; (text after the ';' is just comments)
prog/new "f2"            ; now we start programming our new function
ENTER                    ; copy x. Now there are two x'es on the stack
math/simple/x²           ; calculate x², using one of the x'es
2
*                        ; now we have 2x²
stack/x<->y              ; bring down the other x
3
*                        ; now we have 2x² and 3x on the stack
-                        ; subtract the two, now we have 2x²-3x
1
-                        ; subtract 1, now the stack contains f(x)
prog/finish              ; program finished

Let's see how the program works. Clear the stack, and enter the number 3. Then press "prog/run/f2". The '3' disappears and is replaced by '8'. We have now calculated f(3)=8.

To draw the function, follow the steps above, but select program f2 instead of f1 for drawing.

Quadratic equation

This program solves a second-order polynomial of the type

    ax² + bx + c = 0

The method used is accurate also if b² >> 4ac, as described here. Before running the program, push the coefficients a, b, and c to the stack, in that order. On exit, the stack contains the original coefficients plus the two roots (real or complex).

Program listing:
prog/new "Quad"          ; start recording a new program
stack/RCL st# 1          ; b
math/simple/x²
stack/RCL st# 3          ; a
stack/RCL st# 2          ; c
*
4
*
-
math/simple/sqrt         ; sqrt(b²-4ac)
stack/RCL st# 2          ; b (as the stack moves the index changes)
prog/util/sgn
*                        ; sgn(b)*sqrt(b²-4ac)
stack/RCL st# 2          ; b
+
-2
/                        ; q = -(b+sgn(b)*sqrt(b²-4ac))/2
stack/RCL st# 1          ; c
stack/RCL st# 1          ; q
/                        ; first root, c/q
stack/x<->y              ; q again
stack/RCL st# 4          ; c
/                        ; second root, q/a
prog/finish              ; program finished

Solving an equation for different variables

Assume you have an equation having different variables, for example the equation describing an accelerated motion from a starting height h0, with accelleration a, and initial velocity v0. You want to know the height h at time t. Then the equation looks like

    h = h0 + v0t  + ½at²

To solve this equation for different variables, we make use of some registers, one for each variable, the memory monitor mode and the equation solver, and even function plotting.

Since the built in solve command solves equations for 0, we transform the above equation into

    0 = h0 + v0t + ½at² - h

We use the following memory registers for the different variables, just in order of their appearance in the equation:

M1=h0, M2=v0, M3=t, M4=a, M5=h

The register M0 will have a special role here: It will the determine the variable we want to solve the equation for, e.g. set M0=3 for solving for M3=t.

For following this example, switch on the memory monitor mode ("mode/monitor/mem/<4-7>/<6>") and enter some values:

M0=3, M1=100, M2=10, M4=-9.80665, M5=0

(The 9.80665 can be entered using "special/conv/const/astro/gn".)

Now we enter the equation as a program:
prog/new "Speed"         ; start a new program
mem/RCL 0                ; find out for which variable to solve
prog/mem/STO[x]          ; store value on stack in this variable
clear                    ; remove var number+value
clear
mem/RCL 1                ; start equation: recall h_0
mem/RCL 2                ; recall v_0
mem/RCL 3                ; recall t
*                        ; v_0 * t
+                        ; h_0 + v_0 * t
mem/RCL 4                ; recall a
mem/RCL 3                ; recall t
math/simple/x²           ; t2
*                        ; a * t2
2
/                        ; a * t2 / 2
+                        ; h_0 + v_0 * t + a * t2 / 2
mem/RCL 5                ; h
-                        ; h_0 + v_0 * t + a * t2 / 2 - h
prog/finish

Now we can use the solver to find out which value for t matches the given values for the other variables. To start the solver, we need to specify a lower bound and an upper bound where to search for the zero of the equation, eg:
0                        ; lower bound
100                      ; upper bound
prog/more/solve/Speed    ; call solver for our equation

The result shows that after about 5.65 seconds we will experience a pretty hard landing on the ground. :)

If we wanted to know what initial speed is needed so that a ball starting at a height of 100m is at 50m after 1 second, we would enter:

M0=2 (solve for M2=v0), M1=100; M3=1, M4=-9.80665, M5=50
0                        ; lower bound
100                      ; upper bound
prog/more/solve/Speed    ; call solver

This gives us "nan" as result, since our guess of the bounds was inappropiate. To find out which guess we could use, plot the error of the equation:
0
100
0
100
prog/draw/y=f(x)/Speed

The output looks like there is a zero for negative values of v0. Hence try
-100
0
prog/more/solve/Speed

This gives the result: approx -45.1 So we have to hit the ball quite hard ... :)

One suggestion: The hardest part is to remember which variable is in which register. The memory mode displaying the entered/solved values can be quite helpful for guessing which is which, but probabely you will need a pice of paper here.

Submitted by Goetz Schwandtner

DDD.MMSS conversions

The built-in conversions to and from the DH.MS date-time representation is quite impractical if you are calculating map or stellar coordinates and want to convert between decimal degrees and degrees, minutes and seconds. However, a few small programs using the already built-in conversions can help you out:

Program listing:
prog/new "->DMS"         ; start recording first program
ENTER                    ; make copy of input argument
math/misc/int/trunc      ; integer part
stack/x<>y
math/misc/int/frac       ; fractional part
conv/time/->DH.MS
+
prog/finish              ; first program finished


prog/new "->D"           ; start recording second program
ENTER                    ; make copy of input argument
math/misc/int/trunc      ; integer part
stack/x<>y
math/misc/int/frac       ; fractional part
conv/time/->H
+
prog/finish              ; second program finished


prog/new "DMS+"          ; start recording third program
ENTER                    ; make copy of first argument
math/misc/int/frac       ; fractional part
stack/x<>y
math/misc/int/trunc      ; integer part
stack/x<>st# 2           ; get second argument
ENTER                    ; make copy of second argument
math/misc/int/frac       ; fractional part
stack/x<>y
math/misc/int/trunc      ; integer part
stack/x<>st# 2           ; get first fractional part
conv/time/DH.MS+         ; sum everything...
+
+
prog/finish              ; second program finished

Note: If you are able to enter the sign "»" in the program name, it will magically turn into the sign "→" in the menu. In that case you should call your programs "»D.MS", "»D", and "D.MS+" for clarity.

Biorhythms

The theory of biorhythms is speculative, but slightly entertaining. To program it, we use the fact that real numbers are drawn as a pink line, imaginary parts of complex numbers are drawn as a yellow line, and points where the imaginary and real parts of a complex number coincide, are drawn with a white color. In addition we use the fact that if a program result alternates between values, many graphs can be drawn together. This program is only intended to be "draw"n, if you "run" the program, you will get alternating results.

Program listing:
prog/new "BioRt"         ; start recording a new program
conv/time/now            ; get current time
mem/RCL 0                ; get birthday
+/-
conv/time/DH.MS+         ; subtract dates
conv/time/->H            ; hours since birthday
24
/                        ; days since birthday
+                        ; add x offset (on stack at start of program)
trig/pi
ENTER
+
*                        ; multiply by 2*pi to convert to radians
ENTER
ENTER                    ; 3 copies now on stack
23
/
trig/sin                 ; physical cycle is 23 days
stack/x<->y
28
/
trig/sin                 ; emotional cycle is 28 days
trig/coord/r->cplx       ; make complex number, re=emotional, im=physical
stack/x<->y
33
/
trig/sin                 ; intellectual cycle is 33 days
ENTER
trig/coord/r->cplx       ; make complex number with re=im
mem/RCL 1                ; memory location 1 alternates between 0 and 1
+/-
1
+
mem/STO 1                ; alternating number calculated and stored
prog/util/select         ; select y or z depending on alternating number
prog/finish              ; program finished

Preparations: To try the program, store your birthday in memory location 0, store 0 in memory location 1, make sure calculator is in RAD mode, and push suitable graph limits on the stack, for instance -14, 14, -1.2, 1.2, before finally executing "prog/draw/y=f(x)/BioRt". This will draw the biorhythms for 14 days before and after present. The pink graph shows your emotional cycle, the yellow graph shows your physical cycle, and the white graph shows your intellectual cycle, with zero on the x axis representing current time.

Advanced curve fitting example

Suppose we have a set of data observations and we want to find curve that fits the points:
   x   y
 -------
   0   0
   5   6
  10  10
  20  17
  30  21
  50  27

Looking at the points, the curve that seems to start at the origin, grow linearly at first, and gradually lose its steepness towards the right. One function that behaves this way is:

    y = a ln(bx + 1)

Now we want to find out if such a curve fits the observed data well. The statistics module can fit a curve to the function y = a ln(x) + b. If we add a parameter c to the x values before summing the statistics, we can fit a curve to the function y = a ln(x + c) + b. This curve corresponds to the function above iff. ln(c) = -b/a. Consequently, programming the entering of the above data points with modified x values, and calculating the resulting ln(c) + b/a, we can find our curve using the "solve" operation.

Program listing:
prog/new "fit"           ; start recording a new program
mem/STO 0                ; save input value, c
stat/clear
0 ENTER
0
mem/RCL 0
+
stat/SUM+                ; x1+c,y1
6 ENTER
5
mem/RCL 0
+
stat/SUM+                ; x2+c,y2
10 ENTER
10
mem/RCL 0
+
stat/SUM+                ; x3+c,y3
17 ENTER
20
mem/RCL 0
+
stat/SUM+                ; x4+c,y4
21 ENTER
30
mem/RCL 0
+
stat/SUM+                ; x5+c,y5
27 ENTER
50
mem/RCL 0
+
stat/SUM+                ; x6+c,y6
mem/RCL 0
math/pow/ln              ; ln(c)
stat/result/alnx+b/a,b   ; calculate a,b
/
+                        ; output value, ln(c)+b/a
prog/finish              ; program finished

Running the program with a few test values shows that c=1 and c=20 is located on either side of the solution, and entering those limits before executing "prog/solve/fit", yields the result c=10.76295. Looking at the curve with "stat/result/alnx+b/draw" shows a pretty good fit (although translated 10.76 along the x axis), and the correlation coefficient "stat/result/alnx+b/r" returns 0.9996, which indicates that the data is modeled well by the curve. The coefficient "b" of the original function can be calculated from the coefficients of our current function as eb/a, and the coefficient "a" is the same.

By letting the input value of a program modify a set of statistics, we have used the solve function to perform a curve fitting out of the ordinary. The resulting function is as follows:

    y = 15.724 ln(0.092911x + 1)

Spline drawing

Given a number of 2D control points, a smooth parametric curve consisting of segments of third degree polynomials x=f(t) and y=g(t) can be fitted to the control points. This is called a spline. The calculations are simplified by the use of matrices.

Program listing:
prog/new "Splin"         ; start recording a new program
mem/RCL 0                ; matrix of control points
stack/x<->y              ; t
math/matrix/size
clear                    ; rows = number of control points
3
-                        ; rows-3 = number of curve segments
*                        ; t*(rows-3) = current curve segment
enter
math/misc/int/frac       ; t within this curve segment
mem/STO 2
clear
math/matrix/split        ; extracting control points for this curve segment...
stack/x<->y
clear
4
math/matrix/split
clear                    ; G = [ G0  G1  G2  G3 ]^T  (control points)
mem/RCL 2
3
math/pow/y^x
mem/RCL 2
math/simple/x²
mem/RCL 2
1
math/matrix/concat
math/matrix/concat
math/matrix/concat       ; T = [ t^3  t²  t  1 ]
mem/RCL 1                ; M (spline basis matrix)
*
stack/x<->y
*                        ; [ x  y ] = T*M*G
-1
math/matrix/split        ; x, y
stack/x<->y
trig/coord/r->cplx       ; z = x+yi
prog/finish              ; program finished

Preparations: Create an Nx2 matrix containing the control points and store them in memory location 0. Create a 4x4 spline basis matrix and store it in memory location 1. Push suitable graph limits on the stack before executing "prog/draw/z=f(t)/Splin".

Example set of 2D control points:
     /  2 22 \       ...
     |  7 19 |     | 21  6 |
     | 14 18 |     | 23  0 |
     | 13 22 |     | 26  7 |
     |  2 11 |     | 30 18 |
     |  4  2 |     | 29 22 |
     | 11  2 |     | 25  9 |
     | 18  9 |     | 27  0 |
     | 21  9 |     | 31  5 |
     | 22  9 |     | 34  9 |
     | 19  9 |     | 37  9 |
     | 16  6 |     | 38  9 |
     | 16  2 |     | 35  9 |
     | 20  2 |     | 32  6 |
     | 22  5 |     | 32  2 |
     | 22  7 |     | 36  2 |
     | 23 10 |     | 38  3 |
       ...         \ 39  4 /

Examples of spline basis matrices:
 Catmull-Rom spline:     B-spline:

     / -1  3 -3  1 \         / -1  3 -3  1 \
 1/2*|  2 -5  4 -1 |     1/6*|  3 -6  3  0 |
     | -1  0  1  0 |         | -3  0  3  0 |
     \  0  2  0  0 /         \  1  4  1  0 /

A Catmull-Rom spline interpolates the control points but is only first order continuous. A B-spline does not interpolate the control points (it most often winds between the points), but it is second order continuous, and therefore often smoother.

More

If you think you've made a clever program, send it to me (roarl at users.sourceforge.net), and I might publish it here.