The programming section of the user guide is mostly about functions, and all of the function examples there are simple recordings of button presses, that are played back when the program is run. However, programs can be much more. Not all programs are made for drawing graphs, programs can have multiple inputs and multiple outputs, and programs can be made more complicated with branching and looping.
In the user guide, when explaining what a button does, it is referred to as an "operation". 99% of all operations are programmable, i.e. they can be part of a program. An operation that is part of a program becomes a program "step". Steps are organized vertically, one step on each line. A collection of steps becomes a program. When executing a program, the steps of the program are executed in order top to bottom, unless flow control or conditional operations are used. When executing a program one step at a time, using the ⬇step button, it is called stepping. (The ⬇step button is an example of an operation that is not programmable.)
(After writing this, I realize that the programming examples are best viewed in a large browser, not in a mobile browser. I guess it can't be helped, now, and you just have to be careful when you copy and paste the programs on your mobile. Long programs require planning and documentation in the form of comments, which is why I sometimes edit a program outside PowerCalc, as explained in Pro tips.)
Let's go through some of the operations that are only available during programming. They replace the prog menu while you are in programming ( PROG ) mode.
When running a program, after executing one program step the program normally proceeds and executes the next step. The following operations can be used to alter the normal flow of the program, changing the next step to be executed:
LBL : Sets a program label that can be used as a destination for GTO and GSB from somewhere else in the program.
GTO : Go-to a label. Program execution jumps to the specified label and continues.
GSB : Go-to a subroutine. Like GTO , but the current step in the program is saved, and a subsequent RTN operation will return the program to the step below GSB .
RTN : Return from a subroutine to continue execution from the step below the last GSB operation.
STOP : Stop program execution. All programs stop when execution reaches the end, but this operation will make the program stop in the middle. Whatever is contained in the stack at this point, will be the "output" of the program.
There is more help information on these operations in the pop-up help if you press-and-hold the corresponding button.
Program flow operations don't get you very far without some way to change what is happening based on the current situation. The following operations all have in common that they only execute the next step of the program provided some condition is met:
x=0? x<y? , .. : These operations under prog x? compare either x against 0, or x against y, and execute the next step if the condition is true.
DSE : Decrement, skip if equal. A memory location is decremented, and if it reaches its end value (usually zero) the next step is skipped.
ISG : Increment, skip if greater. A memory location is incremented, and if it passes its end value the next step is skipped. There is a longer explanation in the pop-up help.
Conditional operations are often combined with flow control operations, such as x<0? followed by GSB 1 . In this particular example, if x is negative, we call the subroutine starting with LBL 1 , perhaps in order to do something special with x in that case.
This is most of what we need. With these programming operations at our disposal, we're ready to tackle the most difficult programming tasks.
Let's start with a moderately simple program that is not a function, rather it solves a second-order polynomial equation of the type:
ax² + bx + c = 0
The method used in this program is accurate also if b² ≫ 4ac, as described here. In short, the two roots are c/q and q/a, where:
q = -(b + sign(b)·√(b²-4ac))/2
It will be a program with 3 inputs and 2 outputs. Before running the program, we assume that the coefficients a, b, and c are pushed to the stack, in that order. On exit, the stack will contain the two roots (real or complex).
To record the program, simply press:
prog new "ABC" stack RCL ST y math x² stack RCL ST 3 stack RCL ST z × 4 × - √x stack RCL ST z cplx re prog utils sign × stack R⬆# 3 + -/e 2 ÷ ENTER⬆ stack R⬇# 3 ÷ stack R⬇# 3 x≷y ÷ prog finish
Just kidding. If you want to practice entering a program, you could do that, it is only about 50 button clicks, but it is easy to lose track. If you really are going to do it, then to help you along the way it is a good practice to start programming with some real inputs on the stack (in this case the a, b and c coefficients), just as they would have been before running the program. That way, the stack will show you what is happening while you are programming. Try it by entering e.g. a=1, b=2 and c=-3 before starting to record the program above, like this: 1 ENTER⬆ 2 ENTER⬆ -/e 3 .
Fortunately, there is an easier way. Each programmable operation in PowerCalc has a text representation, and an entire program can be exported as text by copying it from the calculator to the clipboard. Later on, or in another PowerCalc app, the program can be imported by pasting it from the clipboard.
The following program listing is the result of first doing the 50 button clicks above, then pressing mode state prog: copy ABC , and then pasting the result here. Take a look:
Program: ABC
# c \ b \ a (stack at program start)
1: RCLST 1 # b \ c \ b \ a
2: SQR # b²
3: RCLST 3 # a
4: RCLST 2 # c
5: MUL
6: 4
7: MUL # 4ac
8: SUB # b²-4ac \ c \ b \ a
9: SQRT # √(b²-4ac)
10: RCLST 2 # b
11: RE # (in case b is complex, "sign" will give NaN)
12: SIGN # sign(re(b))
13: MUL # sign(b)·√(b²-4ac) \ c \ b \ a
# (now we need b for the last time, so bring it with a stack rotation)
14: ROLLUP_N 3 # b \ sign(b)·√(b²-4ac) \ c \ a
15: ADD
16: -2
17: DIV # q = -(b + sign(b)·√(b²-4ac))/2
18: ENTER # q \ q \ c \ a
19: ROLLDN_N 3 # q \ c \ q \ a
20: DIV # c/q \ q \ a (first root: c/q)
21: XCHGST 2 # a \ q \ c/q
22: DIV # q/a \ c/q (second root: q/a)
Some comments have been added to explain what is going on. Everything after a hash (#) on a line will be ignored when importing. Mostly, the comments explain what the current result in x contains. In addition, to illustrate what is going on with the rest of the stack, the notation x \ y \ z is used to show more stack elements from the bottom up, as if viewing the stack with the head tilted to the right.
To import the program, mark the whole program listing (everything inside the light gray area) and press copy. Then open the calculator and press mode state prog: paste and finally choose an empty program button.
Now, if you want to try it with a=1, b=2 and c=-3, press 1 2 -3 run ABC , and the results should be 1 and -3 .
I mentioned beautiful math, now we are getting to some of it. The gyroid is a 2-dimensional surface that fills the entire 3-dimensional space in a twisty fashion. It has many fascinating properties worthy of googling. In any case, it can be approximated with the following implicit equation:
sin x cos y + sin y cos z + sin z cos x = 0
Is there any way we can make PowerCalc visualize this? Well, there is the prog draw f(x,y)=0 drawing operation. If we start with the x and y input from that drawing mode, and set z to 0, we can draw the gyroid cross section through the plane z=0.
Let's try it with this program:
Program: gyroid
# x \ y (stack at program start)
1: 0
2: ROLLDN_N 3 # x \ y \ z
3: ENTER # x \ x \ y \ z
4: SIN # sin(x) \ x \ y \ z
5: RCLST 2 # y
6: COS # cos(y)
7: MUL # sin(x)·cos(y) \ x \ y \ z
8: XCHGST 2 # y \ x \ sin(x)·cos(y) \ z
9: SIN # sin(y)
10: RCLST 3 # z
11: COS # cos(z)
12: MUL # sin(y)·cos(z) \ x \ sin(x)·cos(y) \ z
13: XCHGY # x \ sin(y)·cos(z) \ sin(x)·cos(y) \ z
14: COS # cos(x)
15: ROLLUP_N 4 # z \ cos(x) \ sin(y)·cos(z) \ sin(x)·cos(y)
16: SIN # sin(z)
17: MUL # sin(z)·cos(x) \ sin(y)·cos(z) \ sin(x)·cos(y)
18: ADD
19: ADD
(Review the section on export and import when it comes to importing this program.)
Let's see what it looks like, by pressing e.g.: -10 10 -10 10 prog draw f(x,y)=0 gyroid :
Promising... For those that googled "gyroid", we see that the result is entirely within what we could expect. The program is probably correct, and it is interesting that we get multiple y values for each x value, it is just not quite "beautiful" yet. (If all you got was a diagonal line, you may have to set the DRG setting to RAD , to switch calculations to radians.)
What if we could look at a different cross section, and interactively change it while drawing, so that we can find a really interesting one? Let's employ both parameterization and matrix calculations to achieve this. We will convert the input coordinates to a vector [ x y 0 ], then rotate it in 3D around the x, y and z axis by parameterized angles in M0, M1 and M2, before calculating the gyroid equation.
In the following program, step 3 to 13 were inserted between step 2 and 3 in the previous program:
Program: gyroid
# x \ y (stack at program start)
1: 0
2: ROLLDN_N 3 # x \ y \ z
3: TO_ROW 3 # [ z y x ]
4: RCL 0 # M0
5: ROT_X # M[3×3]
6: MUL # [ z' y' x' ]
7: RCL 1 # M1
8: ROT_Y # M[3×3]
9: MUL # [ z" y" x" ]
10: RCL 2 # M2
11: ROT_Z # M[3×3]
12: MUL # [ zᐩ yᐩ xᐩ ]
13: BREAK_MATRIX # x \ y \ z
14: ENTER # x \ x \ y \ z
15: SIN # sin(x) \ x \ y \ z
16: RCLST 2 # y
17: COS # cos(y)
18: MUL # sin(x)·cos(y) \ x \ y \ z
19: XCHGST 2 # y \ x \ sin(x)·cos(y) \ z
20: SIN # sin(y)
21: RCLST 3 # z
22: COS # cos(z)
23: MUL # sin(y)·cos(z) \ x \ sin(x)·cos(y) \ z
24: XCHGY # x \ sin(y)·cos(z) \ sin(x)·cos(y) \ z
25: COS # cos(x)
26: ROLLUP_N 4 # z \ cos(x) \ sin(y)·cos(z) \ sin(x)·cos(y)
27: SIN # sin(z)
28: MUL # sin(z)·cos(x) \ sin(y)·cos(z) \ sin(x)·cos(y)
29: ADD
30: ADD
Before starting the drawing operation, remember to parameterize M1, M2 and M3, e.g. to the interval [-π, π], like so:
π math +/- π prog draw param M0 draw param M1 draw param M2 .
(If M1-M3 were parameterized from before, it may be that you have to "turn off" parameterization first, by pressing e.g. draw param M0 for each memory element.)
Again, let's see what it looks like:
Certainly a lot more interesting, and what a lot of fun adjusting those parameters! If you weren't able to find the exact same parameters as I did to generate the graph above, here is a copy of the memory contents that I used:
{
"M0": {
"r": 0.44534840325715362294,
"parameterization": { "min": -3.1415926535897932385, "max": 3.1415926535897932385 }
},
"M1": {
"r": -2.1087161263194204985,
"parameterization": { "min": -3.1415926535897932385, "max": 3.1415926535897932385 }
},
"M2": {
"r": 0.06878757842160526182,
"parameterization": { "min": -3.1415926535897932385, "max": 3.1415926535897932385 }
}
}
To import the memory contents, complete with parameterizations, first copy the entire gray are above, the press mode state mem: paste .
Then, drawing like this should generate the above graph: -10 10 -10 10 prog draw f(x,y)=0 gyroid .
When it comes to pocket calculators, the holy grail has got to be Mandelbrot fractals. If we can do this, we can do anything. With the prog draw z=f(z) operation and it's special RGB output mode, combined with PowerCalc's support for complex numbers, it seems that it should be possible.
If you don't know anything about Mandelbrot fractals you should google it. The basic theory is that for a point, c, in the complex plane, starting with z0 = 0 (or z1 = c), you calculate the sequence zn+1 = zn² + c. The z's will follow a winding path in the complex plane which will either diverge (escape) when |z|>2, or it will continue trapped inside an intricate boundary (called the Mandelbrot set) forever. Depending on how many iterations it takes before the sequence diverges (the escape time), you draw a color for the input coordinate, c, or if the sequence does not diverge you paint it black.
For the first time we will require flow control operations, since fractals are all about looping. To plan the program (and to better understand it later), it can be a good idea to write it down using pseudo-code in a higher level programming language. Here is how it might have looked in C++:
float log10MaxCount = 2; // M0, suitable for parameterization [1, 4]
float countPower = 1; // M1, suitable for parameterization [0.1, 1]
float colorPhaseOffset = 0; // M2, suitable for parameterization [0, 30]
vector<float> mandel(complex c)
{
complex z = c; // M3 = c
int maxCount = round(pow(10,log10MaxCount)); // M4 = maxCount
int countDown = maxCount; // M5 = countDown
loop:
do {
z = z*z + c;
if (abs(z) > 100)
goto calculateColor;
} while (--countDown > 0);
return {0, 0, 0}; // Black
calculateColor:
float count = maxCount - countDown - log2(log2(abs(z)));
float phase = pow(count, countPower);
float red = color(phase, 1, 0);
float green = color(phase, 0.7, 1);
float blue = color(phase, 1.3, -1.5);
return {red, green, blue};
}
float color(float phase, float phaseFactor, float offsetFactor)
{
return sqrt((sin(phase*phaseFactor + colorPhaseOffset*offsetFactor) + 1)/2);
}
Ok, that is already a lot. Let's go through the things that are special when it comes to creating a program for use in PowerCalc:
Some parameters are global to the whole calculation, but are things that we might want to change as we go, typically through parameterization:
M0 = log10(maxCount): If this is parameterized to the range [1, 4], it means maxCount will be 10-10000, which should be a suitable range.
M1 = countPower: Close to the set the iteration count can grow very rapidly. Raising the count to a low power can dampen this and limit the color noise in the resulting image.
M2 = phaseOffset: This parameter is added to the phase of the color calculation. Changing it will gradually modify the colors.
The "mandel" function starts of by calculating the maximum iteration count and setting up a count-down.
The Mandelbrot iteration loop is fairly standard. This is the part of the program that will be iterated thousands of times, so it is important that it is as efficient as possible. The only thing special is that we use a large "escape" limit. This results in a couple of extra iterations every time, but it is necessary to ensure accurate "fractional escape time" calculation.
As we shall see, calculating the colors is what will take up most of the program. First, we use a trick to calculate the fractional escape time, by subtracting log2(log2(|z|)) of the final z value from the iteration count.
Secondly, instead of generating a huge color table, we want to automatically generate an endless shifting palette. This is done rather casually by putting together some sine waves of different frequencies and phase offsets, and the final square root of each color component makes the colors brighter and more pastel-like.
In the final step we convert the red, green and blue color values into a 3D vector. When the drawing operation draw z=f(z) encounters a result like that, it treats it special and draws it as an RGB color.
Of course, we shouldn't have used goto above, but with it the code looks more like the actual PowerCalc program:
Program: mandel
Options: noGrid; aspect=locked1to1
# c (stack at program start)
1: RAD # (ensure RAD mode for the color calculations)
2: STO 3 # M3 = c
3: RCL 0 # log10MaxCount \ c
4: EXP10
5: ROUND # maxCount = round(pow(10,log10MaxCount))
6: STO 4 # M4 = maxCount
7: STO 5 # M5 = countDown
8: CLX # remove maxCount
# z = c (stack at loop start)
9: LBL 0 # loop:
10: SQR # z²
11: RCL 3 # c
12: ADD # z = z²+c
13: ENTER # z \ z
14: ABS # |z| \ z
15: 100 # 100 \ |z| \ z
16: IF_LESS # if (100 < |z|)
17: GTO 1 # goto calculateColor
18: CLX # |z| \ z
19: CLX # z
20: DSE 5 # if (--countDown > 0)
21: GTO 0 # goto loop;
22: CLX # remove z (to avoid cluttering the stack)
23: 0
24: STOP # return 0 (we don't need a vector, 0 will also be drawn as black)
# 100 \ |z| \ z (stack after GTO 1 above)
25: LBL 1 # calculateColor:
26: CLX # |z| \ z
27: XCHGY # z \ |z|
28: CLX # |z|
29: LOG2
30: LOG2
31: NEG # -log2(log2(|z|))
32: RCL 4 # maxCount
33: RCL 5 # countDown
34: SUB
35: ADD # count = maxCount - countDown - log2(log2(|z|))
36: RCL 1 # countPower
37: YPOWX # phase = count^countPower
38: ENTER # phase \ phase
39: 1
40: 0 # 0 \ 1 \ phase \ phase
41: GSB 2 # red = color(phase, 1, 0) \ phase
42: XCHGY # phase \ red
43: ENTER # phase \ phase \ red
44: 0.7
45: 1 # 1 \ 0.7 \ phase \ phase \ red
46: GSB 2 # green = color(phase, 0.7, 1) \ phase \ red
47: XCHGY # phase \ green \ red
48: 1.3
49: -1.5 # -1.5 \ 1.3 \ phase \ green \ red
50: GSB 2 # blue = color(phase, 1.3, -1.5) \ phase \ red
51: TO_COL 3
52: STOP # return [red, green, blue]
# A LBL that you use GSB to is like a small sub-program with its own stack parameters and return value:
# offsetFactor \ phaseFactor \ phase (stack on entry)
53: LBL 2 # color:
54: XCHGST 2 # phase \ phaseFactor \ offsetFactor
55: MUL # phase*phaseFactor \ offsetFactor
56: XCHGY # offsetFactor \ phase*phaseFactor
57: RCL 2 # colorPhaseOffset \ offsetFactor \ phase*phaseFactor
58: MUL # colorPhaseOffset*offsetFactor \ phase*phaseFactor
59: ADD # a = phase*phaseFactor + colorPhaseOffset*offsetFactor
60: SIN # sin(a)
61: 1
62: ADD
63: 2
64: DIV # (sin(a)+1)/2 (color component in range [0, 1])
65: SQRT # sqrt((sin(a)+1)/2) (brighter color component)
66: RTN
A lot of work and a few dozen programming steps later, we are ready to try it. (Notice the Options: clause after the program title. These are graph drawing options set in the press-and-hold pop-up menu of the graph drawing window.)
Starting with: -3 3 -3 3 prog draw z=f(z) mandel , we can pinch-zoom, pan, adjust the iteration limit with M1, adjust the count power exponent with M2 and adjust the colors with M3...
This image was generated on a Samsung S23, PowerCalc drawing in landscape full-screen mode. It took a few minutes to complete.
When it comes to programs like this, that require a special setup of memory contents and parameterization, it can be an advantage to dedicate a whole stack to it. Simply drag the whole stack window to the left and press the + button to create a new, dedicated stack for Mandelbrot calculations.
The image above was made with the following memory contents:
{
"M0": {
"r": 4,
"parameterization": { "min": 1, "max": 4 }
},
"M1": {
"r": 0.39569286406040191654,
"parameterization": { "min": 0.1, "max": 1 }
},
"M2": {
"r": 22.996241867542266846,
"parameterization": { "min": 0, "max": 30 }
}
}
To import these memory contents and parameterizations, copy the entire gray are above, the press mode state mem: paste .
The graphing limits, i.e. the coordinates of the image are given by the following stack contents:
{
"x": 0.11534085499851443731,
"y": 0.11534026022168617259,
"z": -0.770717535200004688,
"3": -0.7707181722452369917
}
To import the stack contents, copy the entire gray are above, the press mode state stack: paste .
Given a number of control points, Cᵢ, a smooth parametric curve consisting of segments of third degree polynomials can be fitted to the control points. This is called a spline. The B-spline is well suited for this, since it can be expressed in matrix form, which will simplify the program. Given 4 control points, C₁ - C₄, the B-spline segment for these control points is given by the following equation:
⎡ -1 3 -3 1 ⎤ ⎡ C₁ ⎤
⎢ 3 -6 3 0 ⎥ ⎢ C₂ ⎥
P(t) = [ t³ t² t 1 ] · 1/6 · ⎢ -3 0 3 0 ⎥ · ⎢ C₃ ⎥
⎣ 1 4 1 0 ⎦ ⎣ C₄ ⎦
Here, t ∈ [0, 1], and Cᵢ and P are row vectors of some dimensionality, for instance 2D vectors. The middle 4×4 matrix multiplied by 1/6 is called the spline basis matrix, B.
We will make use of the following properties of the B-spline:
Two B-spline segments sharing three control points, for instance C₁ - C₄ and C₂ - C₅ will be continuous in the second order derivative. This means that many B-spline segments formed by overlapping control points will be joined together in a smooth way.
Three consecutive coinciding control points will form a sharp angle in the given point.
To draw the spline, we will use the drawing mode prog draw z=f(t) . The input (t) to the program will be in the range [0, 1]. This would fit perfectly if we only had 4 control points. However, with n control points there will be n-3 groups of 4 control points, each forming a segment of the spline. Therefore the input, t, will have to be "stretched out" to cover all the spline segments.
In the following program, we will assume that the control points are stored in M0 and that M1 contains the spline basis matrix. We will form the vector [ t³ t² t 1 ] for the given segment and the corresponding sub-matrix consisting of 4 control points. Then multiply it all together, before converting the 2D vector into a complex number, z, which is the output of the program.
Program: spline
Options: thickness=-1; color=red; theme=dark; aspect=locked1to1
# t (stack at program start)
1: RCL 0 # C \ t
2: XCHGY # t \ C
3: ROWS # rows(C) \ t \ C
4: 3
5: SUB
6: MUL # t·(rows(C)-3) \ C
7: ENTER
8: FRAC
9: STO 2 # t = frac(t·(rows(C)-3))
10: CLX
11: FLOOR # n = floor(t·(rows(C)-3)) \ C
12: MATRIX_SPLIT # C[n..] \ C[1..n]
13: XCHGY
14: CLX # C[n..]
15: 4
16: MATRIX_SPLIT # C[n+4 ..] \ C[n .. n+3]
17: CLX # C[n .. n+3]
18: RCL 2 # t
19: CUBE # t³
20: RCL 2
21: SQR # t²
22: RCL 2
23: 1
24: TO_ROW 4 # [ t³ t² t 1 ] \ C[n .. n+3]
25: RCL 1 # B \ [ t³ t² t 1 ] \ C[n .. n+3]
26: MUL # [ t³ t² t 1 ]·B \ C[n .. n+3]
27: XCHGY
28: MUL # P = [ t³ t² t 1 ]·B·C[n .. n+3]
29: BREAK_MATRIX # y \ x
30: XCHGY # x \ y
31: R_TO_CPLX # x+iy
Now, all we have to do is enter the basis matrix and store it in M1, and invent some control points (4 or more) to be stored in M0.
115 control points later...
We assure you that only legitimate math was used in the production of this image. For fun, compare it to the humble origin of PowerCalc on the right, made for Java-enabled phones more than 20 years ago using the same program and some of the same control points.
So how to achieve straight lines and breaks in the line? Remember that 3 consecutive coinciding control points will create a sharp corner. This means that control points like A, A, A, B, B, B will form a straight line from A to B. If we also include control points of the form [ NaN NaN ], then any graph segments that include the NaN values will not be drawn.
The graph above was made with the following memory contents:
{
"M0": {
"m": [
[ 2, 22 ], [ 7, 19 ], [ 14, 18 ], [ 13, 22 ],
[ 2, 11 ], [ 4, 2 ], [ 11, 2 ], [ 18, 9 ],
[ 21, 9 ], [ 22, 9 ], [ 19, 9 ], [ 16, 6 ],
[ 16, 2 ], [ 20, 2 ], [ 22, 5 ], [ 22, 7 ],
[ 23, 10 ], [ 21, 6 ], [ 23, 0 ], [ 26, 7 ],
[ 30, 18 ], [ 29, 22 ], [ 25, 9 ], [ 27, 0 ],
[ 31, 5 ], [ 34, 9 ], [ 37, 9 ], [ 38, 9 ],
[ 35, 9 ], [ 32, 6 ], [ 32, 2 ], [ 36, 2 ],
[ 38, 3 ], [ 39, 4 ],
[ "NaN", "NaN" ],
[ 6.76, 21.09 ], [ 6.76, 21.09 ], [ 6.76, 21.09 ],
[ 4.96, 28.88 ], [ 4.96, 28.88 ], [ 4.96, 28.88 ],
[ 9.84, 30.01 ], [ 10.74, 26.11 ],
[ 5.86, 24.98 ], [ 5.86, 24.98 ], [ 5.86, 24.98 ],
[ "NaN", "NaN" ],
[ 11.71, 26.33 ], [ 11.71, 26.33 ], [ 11.71, 26.33 ],
[ 10.81, 30.23 ], [ 15.68, 31.35 ],
[ 16.58, 27.46 ], [ 16.58, 27.46 ], [ 16.58, 27.46 ],
[ 17.48, 23.56 ], [ 12.61, 22.43 ],
[ 11.71, 26.33 ], [ 11.71, 26.33 ], [ 11.71, 26.33 ],
[ "NaN", "NaN" ],
[ 16.66, 31.58 ], [ 16.66, 31.58 ], [ 16.66, 31.58 ],
[ 20.40, 24.23 ], [ 20.40, 24.23 ], [ 20.40, 24.23 ],
[ 20.56, 32.48 ], [ 20.56, 32.48 ], [ 20.56, 32.48 ],
[ 24.30, 25.13 ], [ 24.30, 25.13 ], [ 24.30, 25.13 ],
[ 24.45, 33.38 ], [ 24.45, 33.38 ], [ 24.45, 33.38 ],
[ "NaN", "NaN" ],
[ 30.30, 34.72 ], [ 30.30, 34.72 ], [ 30.30, 34.72 ],
[ 25.43, 33.60 ], [ 25.43, 33.60 ], [ 25.43, 33.60 ],
[ 27.23, 25.80 ], [ 27.23, 25.80 ], [ 27.23, 25.80 ],
[ 32.10, 26.93 ], [ 32.10, 26.93 ], [ 32.10, 26.93 ],
[ "NaN", "NaN" ],
[ 26.33, 29.70 ], [ 26.33, 29.70 ], [ 26.33, 29.70 ],
[ 30.22, 30.60 ], [ 30.22, 30.60 ], [ 30.22, 30.60 ],
[ "NaN", "NaN" ],
[ 33.07, 27.15 ], [ 33.07, 27.15 ], [ 33.07, 27.15 ],
[ 31.27, 34.95 ], [ 31.27, 34.95 ], [ 31.27, 34.95 ],
[ 36.15, 36.07 ], [ 37.05, 32.17 ],
[ 32.17, 31.05 ], [ 32.17, 31.05 ], [ 32.17, 31.05 ],
[ "NaN", "NaN" ],
[ 34.59, 31.69 ], [ 34.59, 31.69 ], [ 34.59, 31.69 ],
[ 37.94, 28.28 ], [ 37.94, 28.28 ], [ 37.94, 28.28 ]
]
},
"M1": {
"m": [
[ -0.16667, 0.5, -0.5, 0.16667 ],
[ 0.5, -1, 0.5, 0 ],
[ -0.5, 0, 0.5, 0 ],
[ 0.16667, 0.66667, 0.16667, 0 ]
]
}
}
To wrap it up, here is a repetition of some of the most important things that I have tried to illustrate in the user guide and in these examples, that will make it easier to become proficient when it comes to programming with PowerCalc.
If you are programming a function, put an input value (or several) on the stack before you start programming. That way you can see what is happening on the stack while you are programming, so that you can verify that you are calculating the right thing.
I haven't pointed it out until now, but all the programming examples maintain the following practice: Don't leave garbage on the stack after the calculation is finished. Likewise, don't involve or modify other stack elements than the ones you are using as input (or have generated in the program). To make sure, in addition to preparing input values as described above, you can put a "guard value", e.g. π or something easily recognizable on the stack above your input values. When the program is finished, the guard value should still be there, immediately above the program output.
The advantage is obvious, this makes your program work like the other buttons of the calculator, you can run one program after another, and running the program can be a part of a greater calculation.
So the program does not behave the way you expected? You need to debug it. It is a good thing, then, that PowerCalc is its own Integrated Debugging Environment!
Again, prepare some input values before starting. Press prog edit <your program>. The current program step will be at the bottom of your program, so scroll to the top and touch the first step of the program. Now, you can debug your program step by step with the ⬇step button. You can simultaneously see the program on the left and what is happening to the stack on the right. With the ⬇step button, unlike during normal program entry, the current program step will jump when you encounter flow control and conditional operations, just as it will when the program is actually running.
During programming, when things have gone wrong, or during debugging when you executed a step too much, use stack undo to bring the stack back to its desired state. This will unfortunately insert the undo operation into the program at this position. Simply press step⬅ to remove the undo step from the program, and you are back to where you started.
While using PowerCalc interactively, i.e. in daily use when you press the buttons and see the results, it doesn't cost much to save the entire previous state of the stack for every single button press, in order to be able to undo it later. However, when running a program and especially while graphing, the calculator can perform millions of operations per second. Then undo becomes a drag. Therefore, PowerCalc has an optimization: If the program does not contain any undo operations at all, then undo tracking will be turned off during program execution.
Some quick experimentation shows that the Mandelbrot program above uses almost 4x as much time if it contains an undo operation, well worth avoiding.
Using PowerCalc interactively and while programming is different in some ways. Take for instance the trig DRG button (to switch between degrees, radians or gons for trigonometric operations). Interactively, you can see the current state and then you simply press DRG until the mode becomes what you want. In a program you must consider the possibility that you don't know what the setting is at the start of the program. To get it set correctly you either have to find out what it is programmatically, or you need a special operation that sets the mode directly to what you need it to be.
Therefore, while in PROG mode, the menus prog mode and prog utils contain some operations that only make sense during programming. There are other operations throughout, that may make more sense during programming than in interactive use, such as stack R⬇# and matrix rows .
The tighter you can express a calculation and the fewer operations you use to do it, in general the faster it goes. Therefore, even if converting your calculation to matrix form will be sub-optimal, for instance with respect to the number of multiplications performed, the resulting program may be faster. Of course, if you use many operations to build the matrix before you can use it, the whole program may end up being slower anyway.
Honestly, I haven't found many good reasons to use units in programs, but the main point here is: whatever makes your calculation easier, use it.
(One use of units comes to mind, regarding the DRG state mentioned above. Trigonometric operations, such as sin , use the unit of the argument if the unit is an angular unit. Therefore pressing 30 unit ×unit angle deg trig sin will calculate 0.5 regardless of the DRG state. This can be used to make a program that uses trigonometric operations without the need to modify the DRG state.)
Some programs make use of memory to store intermediate values or to store program parameters. With many programs, you either have to make sure that each program uses different memory locations, or you can simply swipe the whole stack to the left and create a whole new stack. Each stack has its own memory, but programs are shared between stacks. So long as you remember to run a program in the correct stack, they will not interfere.
There is really no limit to the number of stacks you can have, but of course in the end it becomes difficult to keep track of them all.
With several stacks comes the need to migrate values between stacks, and with large programs or large data sets comes the need to make backups. Use copy and paste for this, both of single stack elements but also of memory contents and programs. Sometimes, for very large programs, it can be useful to pull it out into an actual editor to get the full picture. On several occasions I have emailed a program to myself in order to do this.
One tip in this regard: The step numbers at the beginning of each line in programs are just for decoration, they are ignored and can be entirely absent on pasting a program.
Or: Have fun! If you haven't tried any graph drawing examples using parameterizations yet, you should. As it turns out, today's hand-held devices are so powerful that while drawing a graph you can modify function parameters in real-time and immediately see the result.
If you wonder just how graphs are drawn, try graphing a program containing only the operation prob Γx . Into negative values, it soon becomes difficult. Then it becomes apparent that graphs are drawn one dot at a time, with dots placed semi-randomly along the x axis.
And good luck with the programming.