# Reverse Polish Notation Calculator
## Versions
| 1.0.0 | 06-Mar-2024 | Initial version. |
| 1.1.0 | 20-May-2024 | Import values and operations from file with `import` directive or `--import` option. |
| | | Export results to file with `export` directive. |
| | | Define custom functions with `define` directive. |
| | | Sum values in batch mode with `--sum` option. |
| | | Correct floating point errors by rounding to nearest repeating decimal. |
| 1.2.0 | 05-Jun-2024 | Parse and format hexadecimal as `0x89ab`. |
| | | Export hexadecimal in batch mode with `--hex` option. |
| | | Export no separators in batch mode or with `export` directive. |
| 1.3.0 | 15-Mar-2025 | Format and parse time (already in UTC) with "Z". |
| | | Format hexadecimal with comma not underscore separators. |
| | | Export separators in batch mode with `--sep` option. |
| | | Export precision in batch mode with `--dp` option. |
| 1.4.0 | 25-May-2025 | Set named variable to most recent entry with `set` directive, for later use. |
| | | Remove defined functions with `define` directive, and keyword but no body. |
| | | Show defined functions with `define` directive, and no keyword or body. |
| | | Show defined functions with derived parameter hints. |
| | | Improve inline help and stack history output. |
| 1.5.0 | 12-Jun-2025 | Group values on the stack for sequence operations. |
| | | Create and modify sequences with `seq`, `step`, `sort`, `rev` and `flat` operations. |
| | | Apply unary operation to all values with `apply` directive. |
| | | Copy and duplicate values without variable name. |
| | | Undo import file in a single step after `import` directive or `--import` option. |
| | | Undo cast after `plain`, `delta` and `time` operations. |
| | | Show entire stack with `show` command, or 10 most recent values after an operation. |
| | | Show defined variables with `set` directive and no variable name. |
| 1.6.0 | - | Apply measurement units to values, with context specific scaling. |
| | | Apply formatting to interactive session, with `--hex`, `--sep` and `--dp` options. |
| | | Prevent number formatting infinite loops. |
| | | Show detailed unit and apply errors. |
## Contents
* [Versions](#versions)
* [Contents](#contents)
* [Introduction](#introduction)
* [Program Options](#program-options)
* [Program Features](#program-features)
* [Arithmetic Operations](#arithmetic-operations)
* [Floating Point Error Handling](#floating-point-error-handling)
* [Sequence Operations](#sequence-operations)
* [Stack Operations for Sequences](#stack-operations-for-sequences)
* [Bitwise Operations](#bitwise-operations)
* [Time Operations](#time-operations)
* [Unit Operations](#unit-operations)
* [Formatting Commands](#formatting-commands)
* [Stack Commands](#stack-commands)
* [History Commands](#history-commands)
* [General Directives](#general-directives)
* [Comments](#comments)
## Introduction
RPN is a command line reverse Polish notation calculator. As such, it pushes integer and fractional numbers onto a stack, and pops them off for operations, running in interactive mode:
```
$ rpn
rpn> 6 7
rpn> show
6
7
rpn> mul
42
```
It accepts input from files supplied on the command line, running in batch mode:
```
$ cat input.txt
6 7
mul
$ rpn input.txt
42
```
It accepts input from a POSIX shell command pipeline, running in batch mode:
```
```
It writes output to a POSIX shell command pipeline, running in batch mode:
```
$ rpn >output.txt
6 7
mul
$ cat output.txt
42
```
Feature requests are welcome, but it's a hobby project in a language I don't get to use in my day job, so I prefer to do all the development myself.
## Program Options
The `-c` or `--command` option accepts input directly from the command line:
```
$ rpn --command 6 7 mul
42
```
The `--import` option imports values and operations from a text file. This can be used for commonly used custom functions:
```
$ cat defines.txt
define cube 3 pow
define percent 100 div
$ rpn --import defines.txt
rpn> 2 cube
8
```
The `--sum` option causes all results to be summed, if running in batch mode:
```
$ cat numbers.txt
1 2 3 4 5
$ rpn --sum numbers.txt
15
$ rpn --sum --command 1 2 3 4 5
15
```
The `--hex` option causes results to be printed in hexadecimal, if running in batch mode:
```
$ rpn --hex --command 10 10 mul
0x00000064
```
The `--sep` option causes results to be printed with separators, if running in batch mode:
```
$ rpn --sep --command 10 10 pow
10,000,000,000
```
## Program Features
Some operations are binary like `add` and `mul`, some are unary like `neg` and `inv`, some are nullary like `now`, while others operate on the entire stack like `sum` and `prod`. Inline help provides hints on expected inputs and outputs:
```
rpn> help
Arithmetic operations:
N N add,+ N Add two values
N N sub,- N Subtract two values
N N mul,* N Multiply two values
N N div,/ N Divide two values
N N mod,% N Modulo two values
N neg N Find the negative
N inv N Find the inverse
N N pow N Raise to the power
N sqrt N Find the square root
* sum N Sum all values
* prod N Multiply all values
Sequence operations:
N N seq * Generate integer sequence (start to stop)
3 step * Generate integer sequence (start with step to stop)
* sort * Sort stack or sequence
* rev * Reverse stack or sequence
* flat * Flatten entire stack
Bitwise operations:
N N and N Bitwise AND two values
N N or N Bitwise OR two values
N N xor N Bitwise XOR two values
N N shl N Shift left (multiply by power of 2)
N N shr N Shift right (divide by power of 2)
Time operations:
now N Get the current time (in UTC)
N plain N Format as a plain value
N delta N Format as a delta value (duration)
N time N Format as a time value (in UTC)
Formatting commands:
dec Format as decimal values
hex Format as hexadecimal values
sep Include comma separators
nosep Include no separators
N dp Use fixed decimal places
nodp Use free decimal places
Stack commands:
show Show all values on the stack
* clear Remove all values from the stack
N pop Remove a value from the stack
N dup N N Duplicate a value on the stack
N N swap N N Swap two values on the stack
N cut Cut a value to the internal clipboard
N copy N Copy a value to the internal clipboard
paste N Paste a value from the internal clipboard
History commands:
u(ndo) Undo the last operation
r(edo) Redo the next operation
hist Show all undo/redo history
General directives:
import... Import file e.g. "import file.txt"
export... Export file e.g. "export file.txt"
set,=... Set variable, e.g. "set x"
define... Define function e.g. "define cube 3 pow"
apply... Apply to stack or sequence, e.g. "apply 3 pow"
General commands:
units Show unit names and symbols
help Show this help text
```
### Arithmetic Operations
The `add` operation adds two values:
```
rpn> 5.5 2.5 show
5.5
2.5
rpn> add
8
```
The `sub` operation subtracts two values:
```
rpn> 5.5 2.5 show
5.5
2.5
rpn> sub
3
```
The `mul` operation multiplies two values:
```
rpn> 5.5 2.5 show
5.5
2.5
rpn> mul
13.75
```
The `div` operation divides two values:
```
rpn> 5.5 2.5 show
5.5
2.5
rpn> div
2.2
```
The `mod` operation divides two values and finds the remainder:
```
rpn> 5.5 2.5 show
5.5
2.5
rpn> mod
0.5
```
The `neg` operation finds the negative:
```
rpn> 8 show
8
rpn> neg
-8
```
The `inv` operation finds the inverse:
```
rpn> 8 show
8
rpn> inv
0.125
```
The `pow` operation raises to the power:
```
rpn> 3 4 show
3
4
rpn> pow
81
```
The `sqrt` operation finds the square root:
```
rpn> 100 show
100
rpn> sqrt
10
```
The `sum` operation sums all values on the stack:
```
rpn> 1 2 3 4 5 show
1
2
3
4
5
rpn> sum
15
```
The `prod` operation multiplies all values on the stack:
```
rpn> 1 2 3 4 5 show
1
2
3
4
5
rpn> prod
120
```
#### Floating Point Error Handling
RPN uses big fractions for all operations, except when calculating square roots or other fractional powers, when it converts the arguments to floating point. In order to avoid floating point errors, it rounds all results to the nearest repeating decimal. Without this feature, the final result would be something like 2.000000000000000273:
```
rpn> 2 sqrt
1.4142135623730951454746218587388284504413604736328125
rpn> dup mul
2
```
### Sequence Operations
The `seq` operation creates increasing or decreasing sequences of values, popping the start and end values from the stack and generating a step of 1.0 or -1.0:
```
rpn> 1 5 seq
┌ 1 ┐
│ 2 │
│ 3 │
│ 4 │
└ 5 ┘
```
```
rpn> 5 1 seq
┌ 5 ┐
│ 4 │
│ 3 │
│ 2 │
└ 1 ┘
```
The `step` operation does the same thing, but pops the start, step and end values; new values are created with the same meaning (plain, delta or time) as the start value:
```
rpn> 0 0.2 1 step
┌ 0 ┐
│ 0.2 │
│ 0.4 │
│ 0.6 │
│ 0.8 │
└ 1 ┘
```
```
rpn> 0 delta 3600 18000 step
┌ 00.000 ┐
│ 01:00:00.000 │
│ 02:00:00.000 │
│ 03:00:00.000 │
│ 04:00:00.000 │
└ 05:00:00.000 ┘
```
The `sort` operation sorts values on the stack, treating "not a number" values as lower than everything else:
```
rpn> 4 2 1 0 div 3 5 1
4
2
NaN
3
5
1
rpn> sort
NaN
1
2
3
4
5
```
The `rev` operation reverses values on the stack:
```
rpn> 1 2 3 4 5 show
1
2
3
4
5
rpn> rev
5
4
3
2
1
```
The `flat` operation flattens all sequences on the stack:
```
rpn> 1 5 seq 6 8 seq
┌ 1 ┐
│ 2 │
│ 3 │
│ 4 │
└ 5 ┘
┌ 6 ┐
│ 7 │
└ 8 ┘
rpn> flat
1
2
3
4
5
6
7
8
```
#### Stack Operations for Sequences
If the values at the head of the stack are part of a sequence, operations which would otherwise apply to the entire stack (such as `prod` and `sort`) instead apply to that sequence only:
```
rpn> 11 10 5 4 3 2 1 show
11
10
5
4
3
2
1
rpn> sort
1
2
3
4
5
10
11
rpn> prod
13200
```
```
rpn> 11 10 5 1 seq show
11
10
┌ 5 ┐
│ 4 │
│ 3 │
│ 2 │
└ 1 ┘
rpn> sort
11
10
┌ 1 ┐
│ 2 │
│ 3 │
│ 4 │
└ 5 ┘
rpn> prod
11
10
120
```
### Bitwise Operations
The `and` operation performs a bitwise AND on all bits:
```
rpn> 0xffff 0xff00ff hex
0x0000ffff
0x00ff00ff
rpn> and
0x000000ff
```
The `or` operation performs a bitwise OR on all bits:
```
rpn> 0xffff 0xff00ff hex
0x0000ffff
0x00ff00ff
rpn> or
0x00ffffff
```
The `xor` operation performs a bitwise XOR on all bits:
```
rpn> 0xffff 0xff00ff hex
0x0000ffff
0x00ff00ff
rpn> xor
0x00ffff00
```
The `shl` operation shifts left, i.e. multiplies by a power of 2:
```
rpn> 0xff00 hex
0x0000ff00
rpn> 8 shl
0x00ff0000
```
The `shr` operation shifts right, i.e. divides by a power of 2:
```
rpn> 0xff00 hex
0x0000ff00
rpn> 8 shr
0x000000ff
```
### Time Operations
The `now` command gets the current time, showing times in UTC:
```
rpn> now
2025-03-31T12:34:56.789Z
```
The `plain` command converts to an integer or fractional value:
```
rpn> now
2025-03-31T12:34:56.789Z
rpn> plain
1743424496.789
```
The `delta` command converts to a delta value, optionally showing days, hours, minutes, seconds and milliseconds:
```
rpn> 86399 show
86399
rpn> delta
23:59:59.000
```
The `time` command converts to a time value, showing times in UTC:
```
rpn> 1709294400 show
1709294400
rpn> time
2024-03-01T12:00:00.000Z
```
Delta values can be added to or subtracted from times:
```
rpn> 1709294400 time 86400 delta
2024-03-01T12:00:00.000Z
1T00:00:00.000
rpn> sub
2024-02-29T12:00:00.000Z
```
One time value can be subtracted from another:
```
rpn> 1709294400 time 1709208000 time
2024-03-01T12:00:00.000Z
2024-02-29T12:00:00.000Z
rpn> sub
1T00:00:00.000
```
### Unit Operations
Measurement units can be applied to values, in various categories:
* Time (second, minute, hour, day, week, month, year). Note, month and year are defined as 30 and 365.25 days for this purpose.
* Length (metre, inch, foot, yard, mile).
* Area (square unit, hectare, acre).
* Volume (cubic unit, litre, teaspoon, tablespoon, fluid ounce, cup, pint, quart, gallon, barrel) (UK and US versions).
* Speed (distance over time, speed of sound in air at sea level, speed of light in vacuum).
* Mass (gram, ounce, pound, stone, ton).
* Temperature (Kelvin, Celsius, Fahrenheit, Rankine).
* Data (byte, bit).
[SI and related units](https://en.wikipedia.org/wiki/International_System_of_Units) can be specified by name or symbol (e.g. `second` or `s`, `metre` or `m`) with optional [SI prefixes](https://en.wikipedia.org/wiki/Metric_prefix) (e.g. `millisecond` or `ms`, `kilometre` or `km`). The same applies to byte units, but only multiplying prefixes are allowed (e.g. `kilobyte` or `kB`). International units can be specified by name only (e.g. `mile`, `acre`).
Plain values can be cast to specific units, and unit values are converted to equivalent units:
```
rpn> 37 celsius
37 C
rpn> fahrenheit
98.6 F
```
```
rpn> 1 light
1 light
rpn> m/s sep
299,792,458 m/s
```
All equivalent units can be added or subtracted, and RHS values are converted to LHS units. All equivalent units can be divided, and results are stripped of their units, leaving plain values:
```
rpn> 1 foot 6 inch
1 foot
6 inch
rpn> add
1.5 foot
```
```
rpn> 1 mile 1 foot
1 mile
1 foot
rpn> div
5280
```
Some non-equivalent units can also be multiplied or divided, for area, volume and speed values:
```
rpn> 70 mile/hour 30 minute
70 mile/hour
30 minute
rpn> mul
35 mile
```
```
rpn> 144 inch^2 1 foot
144 inch^2
1 foot
rpn> div
12 inch
```
Available units and prefixes can be shown with the `units` command:
```
rpn> units
Prefix units:
Q quetta 10^30
R ronna 10^27
Y yotta 10^24
Z zetta 10^21
E exa 10^18
P peta 10^15
T tera 10^12
G giga 10^9
M mega 10^6
k kilo 10^3
h hecto 10^2
da deca 10
- - 1
d deci 10^-1
c centi 10^-2
m milli 10^-3
u micro 10^-6
n nano 10^-9
p pico 10^-12
f femto 10^-15
a atto 10^-18
z zepto 10^-21
y yocto 10^-24
r ronto 10^-27
q quecto 10^-30
Time units:
s second
minute
hour
day
week
month
year
Length units:
m metre
inch
foot
yard
mile
Area units:
m^2 square-metre (and other square-length units)
hectare
acre
Volume units:
m^3 cubic-metre (and other cubic-length units)
l litre
tsp
tbsp
floz
pint
quart
gallon
barrel
ustsp
ustbsp
usfloz
uscup
uspint
usquart
usgallon
usbarrel
Speed units:
m/s metre/second (and other length/time units)
mach
light
Mass units:
g gram
ounce
pound
stone
ton
Temperature units:
K kelvin
C celsius
F fahrenheit
R rankine
Data units:
B byte
bit
```
### Formatting Commands
The `dec` and `hex` commands format values as decimal and hexadecimal:
```
rpn> 2 32 pow hex
0x0000000100000000
rpn> dec
4294967296
```
The `sep` and `nosep` commands show and hide separators for decimal and hexadecimal:
```
rpn> 2 32 pow hex sep
0x,00000001,00000000
rpn> dec
4,294,967,296
```
The `dp` and `nodp` commands set and cancel fixed precision for decimal:
```
rpn> 2 sqrt
1.4142135623730951454746218587388284504413604736328125
rpn> 0 dp
1
rpn> 3 dp
1.414
rpn> 6 dp
1.414214
rpn> nodp
1.4142135623730951454746218587388284504413604736328125
```
### Stack Commands
The `clear` command removes all values from the stack:
```
rpn> 1 23 456 show
1
23
456
rpn> clear
```
The `pop` command removes a value from the stack:
```
rpn> 1 23 456 show
1
23
456
rpn> pop
1
23
```
The `dup` command duplicates a value on the stack:
```
rpn> 1 23 456 show
1
23
456
rpn> dup
1
23
456
456
```
The `swap` command swaps two values on the stack:
```
rpn> 1 23 456 show
1
23
456
rpn> swap
1
456
23
```
The `cut` and `copy` commands store a value from the stack in the internal clipboard. The `paste` command copies that value back to the stack:
```
rpn> 1 23 show
1
23
rpn> copy
rpn> 456 show
1
23
456
rpn> paste
1
23
456
23
```
### History Commands
The `undo` and `redo` commands undo the last operation, and redo the next operation in the history:
```
rpn> 1 23 456 show
1
23
456
rpn> prod
10488
rpn> undo
1
23
456
rpn> undo
rpn> undo
Start of undo history
rpn> redo
1
23
456
rpn> redo
10488
rpn> redo
End of undo history
```
The `hist` command shows all undo history:
```
rpn> 1 23 456 show
1
23
456
rpn> prod
10488
rpn> undo
1
23
456
rpn> hist
1 23 456 <==
prod
```
### General Directives
The `import` directive imports values and operations from a text file; the `export` directive exports values to a text file. The following creates a file containing the result of the multiplication:
```
rpn> import params.txt
6
7
rpn> mul
42
rpn> export result.txt
```
The `set` or `=` directive sets a named variable to the most recent entry on the stack:
```
rpn> 36 sqrt = x
6 = x
rpn> 49 sqrt = y
6 = x
7 = y
rpn> x y mul
6 = x
7 = y
42
rpn> set xy
6 = x
7 = y
42 = xy
```
Multiple variables can be set on the same line, and variables can be listed with the `set` or `=` directive by itself:
```
rpn> 1 = one 2 = two 3 = three 4 = four 5 = five 6 7 8
1 = one
2 = two
3 = three
4 = four
5 = five
6
7
8
rpn> set
5 = five
4 = four
1 = one
3 = three
2 = two
```
The `define` directive defines a custom function for subsequent use:
```
rpn> define cube 3 pow
rpn> 2 cube
8
```
The `define` directive with a function name but no body undefines the function. The directive with no function name lists defined functions, with hints on expected inputs and outputs, and this information is also included in the inline help:
```
rpn> define cube 3 pow
rpn> define percent 100 div
rpn> define fubar 0 div
rpn> define fubar
rpn> define
Defined functions:
N cube N Function "3 pow"
N percent N Function "100 div"
```
The `apply` directive applies the same operation (or sequence of operations) to each value on the stack:
```
rpn> 1 2 3 4 5 show
1
2
3
4
5
rpn> apply 3 pow
1
8
27
64
125
```
Only operations which pop one input and push one output are allowed:
```
rpn> 1 2 3 4 5 show
1
2
3
4
5
rpn> apply add
Unsupported apply: N N to N
```
When operations are applied to multiple values, a temporary stack is created for each value, so each result is calculated independently. The temporary stack may contain multiple intermediate values or sequences, as long as they reduce down to a single value at the end. This allows us to (for example) find the factorial of each value on the stack:
```
rpn> define fact 1 seq prod
rpn> 1 5 seq flat
1
2
3
4
5
rpn> apply fact
1
2
6
24
120
```
### Comments
It is possible to add a comment to any entered value, or the result of a calculation. Comments remain attached to their values until replaced, and are copied to duplicated values:
```
rpn> 6 7
rpn> mul # the answer
42 # the answer
rpn> dup
42 # the answer
42 # the answer
```