3 ### curses rpn desktop calculator ###
9 # 08-04 14:45 - error dialog (don't mess up screen)
20 my $height = $LINES-3 || 4;
21 my $width = $COLS || 42;
23 my @val = (0, 0); # val, frac
24 my $nopush = 1; # 0=push and reset next; 1=reset next; 2=do nothing
30 numb => 0, # fixed scientific engineering
31 card => 1, # degrees radians grades
32 coord => 0, # cartesian polar spherical
33 complex => 0, # real complex
38 [qw(quit base>1 math>8 mode>7)],
39 [qw(main>0 dec bin oct hex logic>3 bit>4)], #1 base
40 [qw(main>0 log alog ln exp sin cos tan 0 asin acos atan)], #2 math
41 [qw(base>1 and or xor not)], #3 base logic
42 [qw(base>1 rl sl asr sr rr)], #4 base bit
43 [qw(base>1 rlb slb srb rrb)], #5 base byte
44 [qw(main>0 sq sqrt ^ xroot)], #6
45 [qw(main>0 number_format angle_measure coord_system)], #7 mode
47 vector> matrix> list> hyperbolic>9 real>10 base>1
48 probability> fft> complex> constants>
51 sinh cosh tanh asinh acosh atanh
53 )], #9 math hyperbolic
56 abs sign mant xpon ip fp
57 rnd trnc floor ceil r>d d>r
60 tools> length> area> volume> time> speed>
61 mass> force> energy> power> pressure> temperature>
62 electric_current> angle> light> radiation> viscosity>
69 "\033\117\120" => 1, # f1
70 "\033\133\061\061\176" => 1, # f1
71 "\033\117\121" => 2, # f2
72 "\033\133\061\062\176" => 2, # f2
73 "\033\117\122" => 3, # f3
74 "\033\133\061\063\176" => 3, # f3
75 "\033\117\123" => 4, # f4
76 "\033\133\061\064\176" => 4, # f4
77 "\033\133\061\065\176" => 5, # f5
78 "\033\133\061\067\176" => 6, # f6
79 "\033\133\061\070\176" => 7, # f7
80 "\033\133\061\071\176" => 8, # f8
81 "\033\133\062\060\176" => 9, # f9
82 "\033\133\062\061\176" => 10, # f10
83 "\033\133\062\063\176" => 11, # f11/F1
84 "\033\133\062\064\176" => 12, # f12/F2
85 "\033\133\062\065\176" => 13, # F3
86 "\033\133\062\066\176" => 14, # F4
87 "\033\133\062\070\176" => 15, # F5
88 "\033\133\062\071\176" => 16, # F6
89 "\033\133\063\061\176" => 17, # F7
90 "\033\133\063\062\176" => 18, # F8
91 "\033\133\063\063\176" => 19, # F9
92 "\033\133\063\064\176" => 20, # F10
93 "\033\133\062\063\073\062\176" => 21, # F11
94 "\033\133\062\064\073\062\176" => 22, # F12
100 '_' => 'chs', # change sign
101 'e' => 'eex', # exponent
102 "\033\133\062\176" => 'swap', # ins
103 chr(27).chr(91).chr(51).chr(126) => 'clx', # del
104 chr 127 => '<-', # backspace
105 chr 8 => '<-', # backspace
106 "\014" => 'refresh', # ^L
107 "\033\133\110" => 'refresh', # home
115 chr(27).'s' => 'asin',
117 chr(27).'u' => 'acos',
119 chr(27).'t' => 'atan',
121 chr(27).'l' => 'alog',
123 chr(27).'n' => 'exp',
129 # 'digit' => [-2, sub {
130 # if ($val[1] *= 10) {
131 # $val[0] += $_/$val[1];
134 # $val[0] = $val[0]*10+$_;
137 'digit'=> [-2, sub { $val[0] = ($val[1] *= 10) ? $val[0]+$_/$val[1] : $val[0]*10+$_ }],
138 '.' => [-2, sub { $val[1] = 1 }], # decimal point
139 'eex' => [-2, sub {}], # exponent
140 'chs' => [1, sub {$val[0] = -$val[0]}], # negative
143 $val[0] = ($val[1] = int $val[1]/10)
144 ? int($val[0]*$val[1])/$val[1] : int $val[0]/10
146 'clx' => [0, sub {@stack = (); @val = (0, 0); $nopush = 1}], # clear all
149 unshift @stack, $val[0];
153 'swap' => [1, sub {($val[0], $stack[0]) = ($stack[0], $val[0])}], # swap x<->y
155 '=' => [1, sub {$var{a} = $val[0]}], # copy
156 '>' => [1, sub {$var{a} = $val[0]; $val[0] = shift @stack}], # assign
158 '+' => [2, sub {$val[0] += shift @stack}], # addition
159 '-' => [2, sub {$val[0] = shift(@stack) - $val[0]}], # substraction
160 '*' => [2, sub {$val[0] *= shift @stack}], # multiplication
161 '/' => [2, sub {$val[0] = shift(@stack) / $val[0]}], # division
162 'mod' => [2, sub {$val[0] = shift(@stack) % $val[0]}], # modulo
164 'sqrt' => [1, sub {$val[0] = sqrt $val[0]}], # square root
165 'sq' => [1, sub {$val[0] *= $val[0]}], # squared
166 '^' => [2, sub {$val[0] = shift(@stack) ** $val[0]}], # exponentiation
167 'xroot'=> [2, sub {$val[0] = shift(@stack) ** (1/$val[0])}], # x-root of y
169 'log' => [1, sub {$val[0] = log($val[0]) / log(10)}], # logarithm
170 'alog' => [1, sub {$val[0] = 10 ** $val[0]}], # 10^x
171 'ln' => [1, sub {$val[0] = log $val[0]}], # natural logaritm
172 'lnp1' => [1, sub {$val[0] = log($val[0]+1)}], # ln(x+1)
173 'exp' => [1, sub {$val[0] = exp($val[0])}], # e^x
174 'expm' => [1, sub {$val[0] = exp($val[0])-1}], # exp(x)-1
176 'sin' => [1, sub {$val[0] = sin $val[0]}], # sine
177 'asin' => [1, sub {$val[0] = atan2($val[0], sqrt(1 - $val[0]*$val[0]))}], # inverse sine
178 'cos' => [1, sub {$val[0] = cos $val[0]}], # cosine
179 'acos' => [1, sub {$val[0] = atan2(sqrt(1 - $val[0]*$val[0]), $val[0])}], # inverse cosine
180 'tan' => [1, sub {$val[0] = sin($val[0]) / cos($val[0])}], # tangent
181 # 'atan' => [1, sub {}], # arctangent
183 '%' => [2, sub {$val[0] /= shift(@stack)}], # percentage
184 '%ch' => [2, sub {$val[0] = 100*(shift(@stack)-$val[0])/$val[0]}], # percentage change
185 '%t' => [2, sub {$val[0] = 100*$val[0]/shift(@stack)}], # percentage total
187 'and' => [2, sub {$val[0] = shift(@stack) & $val[0]}], # bitwise and
188 'or' => [2, sub {$val[0] = shift(@stack) | $val[0]}], # bitwise or
189 'xor' => [2, sub {$val[0] = shift(@stack) ^ $val[0]}], # bitwise xor
190 'not' => [2, sub {$val[0] = ~$val[0]}], # bitwise not
192 'abs' => [1, sub {$val[0] = abs $val[0]}], # absolute #todo
193 'sign' => [1, sub {$val[0] = $val[0] <=> 0}], # sign
194 'ip' => [1, sub {$val[0] = int $val[0]}], # integer part
195 'fp' => [1, sub {$val[0] -= int $val[0]}], # fractional part
197 'rnd' => [1, sub {local $_ = 10**shift @stack; $val[0] = int(($val[0]+.5)*$_)/$_}], # round
198 'trnc' => [1, sub {local $_ = 10**shift @stack; $val[0] = int($val[0]*$_)/$_}], # truncate
199 'floor'=> [1, sub {$val[0] = int $val[0]}], # floor
200 'ceil' => [1, sub {$val[0] = int $val[0]+.9999}], # ceil
203 local $_ = shift @stack;
204 $val[0] = $_ if $_<$val[0];
207 local $_ = shift @stack;
208 $val[0] = $_ if $_>$val[0];
211 'dec' => [0, sub {$set{base} = 10}], # decimal
212 'bin' => [0, sub {$set{base} = 2}], # binary
213 'oct' => [0, sub {$set{base} = 8}], # octal
214 'hex' => [0, sub {$set{base} = 16}], # hexadecimal
215 'base36' => [0, sub {$set{base} = 36}], # alphanumerical
221 my ($val, $base) = @_;
222 return $val if $base==10;
225 my $frac = $val-$int;
229 while ($int>$base**10) {
234 my $char = $int%$base;
235 $txt = ($char<10 ? $char : chr($char+55)).$txt;
239 $txt .= '.' if $frac>0;
240 for (my $i = 0; length $txt<$width-2 && $frac>0; $i++) {
242 my $char = int $frac;
244 $txt .= $char<10 ? $char : chr($char+55);
247 $txt .= 'e'.showval($exp, $base) if $exp;
254 addstr($height-$_, 1, "$_: ".showval($stack[$_], $set{base}));
257 clrtoeol($height-$#stack-1, 1);
261 clrtoeol($height+2, 1);
262 for (grep exists $menu[$_], 1..$set{menushow}) {
263 my $sub = (my $s = $menu[$_]) =~ s/>\d+$//;
264 addstr($height+2, $width/$set{menushow}*($_-1), $_);
269 } # display menu txts
277 addstr($height+1, 0, "> ");
280 addstr($height+1, 2, showval($val[0], $set{base}));
286 while (defined (my $key = ReadKey -1)) {
288 } # read additional keys
291 exists $alias{$_} and $_ = $alias{$_};
292 exists $falias{$_} and $_ = $menu[$falias{$_}];
294 if (exists $action{$_} or /^\d$/) {
295 my ($type, $cmd) = @{ $action{$_} || $action{digit} };
297 unshift @stack, $val[0] and showstack() unless $nopush;
298 @val = (0, 0) if $nopush<2; # replace current
302 $nopush = 0 if $type>0;
303 showstack() if $type>=0;
306 elsif ($_ eq 'quit') {
309 elsif ($_ eq 'refresh') {
314 @menu = @{ $menus[$1] };
320 addstr($height+1, 0, "* error: ".join(' ', map ord, split //, $_)." *");
325 ReadKey; # wait for confirm
326 1 while defined (ReadKey -1); # clear key buffer
327 goto DRAW; # screen messed up