For ages, spread sheet programs have been closely associated with financial calculations done by typical end-users. But it has shown that there is also hacker's work which can be done with them, like calculate monitor timings for various resolutions, produce convincing time statistics which justify the lack of documentation or the need for a budget increase to your employer. This first part of this user guide explains how the various functions of teapot are used, whereas the second part gives an introduction to spread sheets and explains the expression evaluator and its functions.
teapot
(Table Editor And Planner, Or: Teapot),
is copyright (c) by Michael Haardt, 1995 - 2006 and copyright (c) by Joerg Walter,
2009-2010. The implementation of clocked expressions is modelled after the description
of clocked evaluation in the PhD work of Jrg Wittenberger
<joerg.wittenberger@inf.tu-dresden.de> at the University of Technology
in Dresden, Germany. The German message catalog was contributed by
Guido Msch, the Dutch
catalog by Wim van Dorst
<baron@clifton.hobby.nl> and the Ukrainian catalog by Volodymyr M. Lisivka <lvm@mystery.lviv.net>.
The trigonometric functions were inspired by Koniorczyk Mtys <kmatyas@cs.elte.hu>.
The context output format was contributed by Marko Schuetz <MarkoSchuetz@web.de>.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
_. +-----+-----+-----+-----+-----+ /| z +-----+-----+-----+-----+-----+ | / +-----+-----+-----+-----+-----+ |-+ +-------------> x | | | | | |-+ | | +-----+-----+-----+-----+-----+ |-+ | | | | | | |-+ | | +-----+-----+-----+-----+-----+ |-+ | | | | | | |-+ | | +-----+-----+-----+-----+-----+ |-+ | | | | | | |-+ | +-----+-----+-----+-----+-----+ v yYou can think of cells as variables, which value is the value of an associated expression. The expression may be constant, like 1.23, or it may be a function of other cell values. The advantage compared to a programmable calculator is that if you change a number, you directly see all changes in other cells caused by that. Often this allows you to get a feeling how much you may change basic sizes with still getting satisfying results without having to solve the problem analytically.
Spread sheets offer many editing functions in order to modify, clear, copy and move cells or blocks of cells. Besides the usual mathematical functions, there are functions which work on blocks of cells, like calculating the sum of a block or counting all non-empty elements. Further there are functions working on character strings, because most likely you also want text besides numbers. The next section will introduce you to some of these by examples.
teapot is a traditional spread sheet and a typical UNIX program, because it does just one thing: Calculations. It does not include any graphics functions and never will, but it allows to export data in many formats, so you can use your favourite graphics software.
NLSPATH
and the LC_*
variables
are set right for your locale. Now start the program without any
arguments:
teapotYou see an empty sheet with the cell cursor being at the upper left corner. Further, the status line tells you that this cell is really empty:
E @(0,0,0)=
The E
means that you can edit the sheet. A V
would mean
that you could only view its contents. The meaning of @()
will be
explained soon. You are now in the command mode of teapot. Now type
return to edit this cell. A complete list of command mode functions
will be given later. A prompt will appear below the status line:
Cell contents:
1
Now the cell at position 0,0,0 has the integer
constant 1. The status line shows you the cell contents, whereas in the
sheet you see its value. Since constants are identical with their values,
both are 1. Now move the cell cursor down one row and edit that cell,
giving it the integer constant 41
.
Now that you have two numbers, move the cell cursor to cell 0,2,0 and give that cell the following contents:
Cell contents:
@(0,0,0)+@(0,1,0)
If you were confused about the difference between contents and value of
a cell, it should become more clear now: The status line shows the contents,
which is the arithmetic expression to calculate the sum of two cells,
whereas in the sheet you see the value of that expression: 42, which
was to be expected. @(
x,
y,
z)
is a function which takes three coordinates and returns the value of the
cell at the given position.
As you can see, the arithmetic expression is not too readable. If you would move cells around, it would not even work any more. For these reasons, you can use symbolic names instead of coordinates, called labels. When used in an expression, a label is like a pointer to a cell, its data type is location. Move to cell 0,0,0 and use / (slash) in command mode to get into the main menu. Depending on your screen size, you may not see all of it. In this case, move the highlighted block right (or left) to scroll through it and to see all items. Now change its label attribute: A)ttributes, L)abel:
Cell label:
Paper
Then go one cell down and change its label to Tapes. After,
move again one cell down and change the expression to:
Cell contents:
@(Paper)+@(Tapes)
As you see, you can call the function @
with three integer values
or with one location value. Now the expression is more understandable,
at least to you. To someone else, the sheet only contained three numbers,
so a little text should be added. To accomplish that, a new column needs to
be inserted: B)lock, I)insert, C)olumn, W)hole column. The last menu
item means that you want to insert a whole new column, not only a partial
column. If you move the cursor around, you will see that everything is
still fine, because you used labels. Go to cell 0,0,0 and edit it:
Cell contents:
"Paper:"
This is how you enter strings. A string is a data type on its own, don't
confuse this with labels. If you feel like it, leave the quotes and the
colon away, and you will see the difference, because the result will not
be a string, but the value of the label Paper
, which is &(1,0,0)
.
Now change the cells below to "Tapes:" and "Result:". This
is something that is understandable to others, too.
As the last step, save your work sheet to a file: F)ile, X)DR. The native file format is XDR, so chose that. Up to now, your sheet does not have a name, so you will be prompted for one:
Save sheet to XDR file:
first_step
Unless you see an error message after,
your sheet is written to a file.
If you have come this far, quit (from the main menu) and you have successfully completed your first steps on using teapot. Now you know cells, the difference between contents and values, you learned that labels are a good thing and you can do simple cell modifications as well as saving your work. This is enough for most applications. If the capabilities described in the next section confuse you, then it is unlikely that you need them really. Just skip that section and don't worry about it.
You may wonder what happens if you have circular dependencies, i.e. you have a cell which evaluates to its own value plus one. Well, the answer is that it depends on the order in which you create this cell. If you first give it the value 1 and after edit it to contain the expression which refers to itself plus 1, then you will find that each recalculation, like after editing other cells, will increase the value. While this may be funny, it is certainly not useful as you can not reset the cell and you have little control of its development.
What you really want is a base value and an iterative expression along with a way to control the recalculations. teapot supports this by allowing two expressions per cell. The expressions you have used so far are the ones which evaluate to the base values. Each time you edit a cell, the whole sheet will be reset, which means that all results are recalculated using the base values. After, you can clock the sheet, which is why the iterative part is also called clocked expression. A clock is an atomic operation, which means that all cell results will be recalculated in a way that the new result will only show after the entire recalculation.
An examples will demonstrate how to make use of this feature.
The notation x -> y means that x is the base
expression and y is the clocked expression. Don't let
this confuse you, as both are entered separately: teapot does
not have an -> operator, but it displays the cell contents this
way for increased overview. So, give the cell a base expression
of 1
and a clocked expression of @(0,0,0)+1
(using
ESC-Enter or Meta-Enter) and you
will see:
@(0,0,0)=1 -> @(0,0,0)+1
The sheet is currently in reset condition and the result is 1. Now
clock it and you will see how the value increases.
After this introductional chapter, you should be familiar with the basic concepts in spread sheets. The next chapters explain all functions available in detail. You should read them to get an overview of the possibilities offered by teapot. Finally, we will come back to using teapot by showing some common problems and their solutions.
Function key | ASCII key | function |
Next line | ^n | cursor down |
Previous line | ^p | cursor up |
Begin | ^a | cursor to column 0 |
End | ^e | cursor to last column |
+ | cursor to next layer | |
- | cursor to previous layer | |
< | cursor to line 0 | |
> | cursor to last line | |
_ | cursor to sheet 0 | |
* | cursor to last sheet | |
^x < | one page left | |
^x > | one page right | |
F10 | / | main menu |
F2 | save menu | |
F3 | load menu | |
^x ^r | load in native format | |
Enter | ^j, ^m | edit cell contents |
", @, digit | overwrite cell contents | |
ESC Enter | ESC ^j, ESC ^m | edit clocked cell contents |
Backspace | ^h | edit cell contents |
. | mark blocks (see below) | |
^l | redraw screen | |
^y | paste (reinsert marked) block | |
^r | reset sheet | |
^s | clock sheet | |
ESC z | save and quit | |
^x ^c | quit | |
Next page | ^v | one page down |
Previous page | ESC v | one page up |
Cancel | ^g, ^c | abort current action |
Function key | ASCII key | function |
Previous character | ^p | go to previous line |
Forward character | ^f | move cursor right |
Begin | ^a | move cursor to column 0 |
End | ^e | move cursor to last column |
Enter | ^j, ^m | finish editing |
^l | redraw screen | |
^t | transpose character | |
^\ | goto matching paren | |
Cancel | ^g, ^c | abort editing |
Backspace | ^h | delete previous character |
Delete | ^?, ^d | delete current character |
Insert | toggle insert mode |
Aborting line editing means that you will get right back to command mode, whatever you started doing will have no effect.
0 | 0 | 1 |
0 | 1 | "one" |
1 | 2 | "two" |
2 | 3 | "three" |
3 | 4 | "four" |
R)ow
, because that is how we want to sort this block.
The X position of the sort key vector is 0, because the column 0 contains
the numbers. The Z position is 0, too, because those numbers are on sheet 0.
Now chose D)escending
as direction. At this point, you could add
a secondary key or decide to sort the block by the keys entered so far.
Use S)ort region
to sort it. That's it, the screen should look like
this now:
0 | 0 | 1 |
0 | 4 | "four" |
1 | 3 | "three" |
2 | 2 | "two" |
3 | 1 | "one" |
.asc
.
FORMAT FREE
.
The field separator is a tab or comma, strings are preferably but
not necessarily enclosed in double quotes and decimal numbers have a
dot to mark the fractional part. The default extension is .txt
.
When loading CSV files, the sheet will not be cleared and the data will
be load relative to the current cursor position. GCSV is the German
variation, which uses semicolons for separating fields and a decimal
comma instead of a decimal point.
On load, strings without quotes and with a 0x prefix followed by
hexadecimal digits will be converted to integers.
.sc
.
.wk1
.
.doc
.
You will have to use soelim(1) to eliminate the.TS
options;
.so
filename.TE
.so
requests before the tbl run. The options;
are optional.
If you use GNU roff, you will need to eliminate .lf
requests,
because this GNU roff extension confuses GNU tbl:
Alternatively, you can generate a stand-alone document, which needs no further operations to format and print. Note: If no block is marked, the whole sheet will be saved, not only the current cell. The default extension issoelim
file| grep -v '^\.lf'
.tbl
.
Alternatively, you can generate a stand-alone document, which needs no further operations to format and print. Note: If no block is marked, the whole sheet will be saved, not only the current cell. The default extension is\include{
filename}
.tex
.
Alternatively, you can generate a stand-alone document. Note: If no
block is marked, the whole sheet will be saved, not only the current cell.
The default file extension is .html
.
&(10,2)
to go to cell
10,2 of the current layer or you could enter the name of a label you
want to go to. Relative movements are no problem, either.
goto
location
from
location
to
location
sort-x
d|a y z [ d|a y z ... ]
sort-y
d|a x z [ d|a x z ... ]
sort-z
d|a x y [ d|a x y ... ]
relative
to the first cell of the marked block.
Up to eight sort keys can be specified.
save-tbl
file
save-csv
file
save-latex
file
save-context
file
save-html
file
load-csv
file
load-gcsv
file
goto
location.
This is the same functionality as the interactive load described in
subsection 5.12.4.
bold
marks literals which have to be entered
exactly as shown. Italic mark place holders, they are not to be
taken literally.
"This is a string"
. A double quote can be part of the
string, if it is quoted using a backslash: "\""
. If you want
the backslash to appear in the output instead of quoting the next
character, use it to quote itself: "\\"
. A single minus
character on the screen will be output as a minus. If you want a
hyphen character in the output, then use two minus characters for that.
42.0
42
&()
function have this type, but
there are no location constant literals.
error()
. An error
always has an assigned error message. Functions and operators, when
applied to a value of the type error, evaluate to just that value. That
way, the first error which was found deep inside a complicated
expression will be showed.
<
y
<=
y
>=
y
>
y
==
y
~=
y
!=
y
+
y
+
for that.
-
y
-
y. If y is empty, the
result is x.
*
y
*
for that.
/
y
%
y
^
y
-
x
-
x if x is a number. If x is
empty, the result will be empty, too.
(
expression)
(
argument,
...)
@(0,0,0)
instead of @(integer 0, integer 0, integer 0)
. If no type is
given for the result of a function, it means the result type depends on
the arguments. Brackets mark optional arguments.
@(
[integer x][,
[integer y][,
[integer z]]])
@(
location l)
&(
[integer x][,
[integer y][,
[integer z]]])
$(
string env)
abs(
float x)
abs(
int x)
acos(
double|int x)
arcosh(
double|int x)
arsinh(
double|int x)
artanh(
double|int x)
asin(
double|int x)
atan(
double|int x)
clock(
integer condition,
[location[,
location])
cos(
double|int x)
cosh(
double|int x)
deg2rad(
double|int x)
e()
error(
string message)
eval(
location)
eval()
. This function may not be used nested any deeper than
32 times.
float(
string s)
frac(
float x)
int(
float x[,
integer neg,
integer pos])
neg, pos | result ---------+---------------------------------- < -1 | next smaller integer value 0 | cut fractional part off (default) 1 | round upward > 1 | next larger integer value
int(
string s)
len(
string s)
log(
double|int x[,
double|int y)
max(
location l1,
location l2)
min
does for the minimum.
min(
location l1,
location l2)
@(min(
l1,
l2))
.
n(
location l1,
location l2)
poly(
float|integer x,
float|integer cn[,
...])
rad2deg(
double|int x)
rnd()
sin(
double|int x)
sinh(
double|int x)
strftime(
string f [,
integer t])
string(
location l)
string(
integer x)
string(
float x[,
[integer precision][,
scientific]])
strptime(
string f ,
string datetime)
substr(
string s,integer x,integer y)
sum(
location l1,
location l2)
tan(
double|int x)
tanh(
double|int x)
x(
[location l])
y(
[location l])
z(
[location l])
@
function for
relative relations to other cells.
0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1You expect to see 1.0 as result, and indeed that is what you get. Now you compare this result to the constant 1.0, but surprisingly for many users, the result is 0. Appearantly, 1.0 is unequal 1.0 for teapot.
This is not a bug in teapot, in fact it is not a bug at all. The problem is, that 0.1 (1.0/10.0) does not have an exact representation in binary floating point arithmetic, similar to how 1.0/3.0 does not have an exact representation in decimal arithmetic (or binary, for that matter). As such, a value very close to 0.1 is used, but when displaying it, it will be rounded to 0.1. The result is obvious, adding this number which is a little different from 0.1 ten times leads to a result very close to but not quite 1.0. Since it is so close, displaying it rounded to only a few digits precision shows 1.0.
To solve the comparison problem, teapot has the operator ~=
, which compares
two floating point values apart from the last significant bit. Use this
operator to compare the two values from above and the result will be 1,
meaning they are about equal. Don't assume that a number which can be
expressed with a finite number of decimal digits will be represented exactly
in binary floating point arithmetic.
But don't worry. :) The answer is, that conditional evaluation
comes for free with teapot's orthogonal cell addressing.
As an example, depending on the cell
labelled X
being negative or not, you want the result to be the
string "BAD
or "GOOD"
. This is the solution:
eval(&((@(X)>=0)+x(BAD),y(BAD),z(BAD)))The cell labelled
BAD
contains the string "BAD"
, its right
neighbour contains the string "GOOD"
. If you have nested
conditions, you could weight them with 1, 2, 4 and so on to address
a bigger range of cells. Alternatively, you could make use of all
three dimensions for nested conditions.
digit | ::= | 0 | .. | 9 |
hex_digit | ::= | 0 | .. | 9 | a | .. | f |
octal_digit | ::= | 0 | .. | 7 |
decimal_integer | ::= | digit { digit } |
hex_integer | ::= | 0x hex_digit { hexdigit } |
octal_integer | ::= | 0 octal_digit { octdigit } |
integer | ::= | decimal_integer |
| hex_integer | ||
| octal_integer | ||
float | ::= | digit { digit } [ . ] { digit } |
[ e | E [ + | - ] digit { digit } ] | ||
quoted_character | ::= | \ any_character |
character | ::= | any_character | quoted_character |
string | ::= | " { character } " |
identifier_character | ::= | _ | @ | & | . | $ | alpha_character |
identifier | ::= | identifier_character |
{ identifier_character | digit } | ||
function | ::= | identifier ( [ term ] { , [ term ] } ) |
label | ::= | identifier |
parenterm | ::= | ( term ) |
negterm | ::= | - primary |
primary | ::= | function | label | parenterm | negterm |
powterm | ::= | primary { ^ primary } |
mathterm | ::= | powterm { / | * | % powterm } |
factor | ::= | mathterm { + | - mathterm } |
term | ::= | factor {< | <= | >= | > | == | != factor } |