old-www/LDP/LG/issue69/spiel.html

697 lines
24 KiB
HTML

<!--startcut ==============================================-->
<!-- *** BEGIN HTML header *** -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML><HEAD>
<title>Numerical Workbenches LG #69</title>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
ALINK="#FF0000">
<!-- *** END HTML header *** -->
<CENTER>
<A HREF="http://www.linuxgazette.com/">
<IMG ALT="LINUX GAZETTE" SRC="../gx/lglogo.png"
WIDTH="600" HEIGHT="124" border="0"></A>
<BR>
<!-- *** BEGIN navbar *** -->
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="qubism.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue69/spiel.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="lg_backpage69.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
<!-- *** END navbar *** -->
<P>
</CENTER>
<!--endcut ============================================================-->
<H4 ALIGN="center">
"Linux Gazette...<I>making Linux just a little more fun!</I>"
</H4>
<P> <HR> <P>
<!--===================================================================-->
<center>
<H1><font color="maroon">Numerical Workbenches</font></H1>
<H4>By <a href="mailto:cspiel@hammersmith-consulting.com">Christoph Spiel</a></H4>
</center>
<P> <HR> <P>
<!-- END header -->
<p>Some people think GNU/Linux is a good operating system, but has not enough
applications to make it succeed in the market. Although this might be true for
the desktop area, it is certainly wrong for numerical workbenches. In this
field GNU/Linux users have many different (and excellent) choices -- in
fact too many to introduce them all. Therefore, this series of articles
introduces three outstanding applications:</p>
<dl>
<dt><strong><a name="item_GNU%2FOctave_2%2E1%2E34">GNU/Octave
2.1.34</a></strong><br>
</dt>
<dd><a href="http://www.che.wisc.edu/octave/">
http://www.che.wisc.edu/octave/</a></dd>
<dt><strong><a name="item_Scilab_2%2E6">Scilab 2.6</a></strong><br>
</dt>
<dd><a href="http://www-rocq.inria.fr/scilab/">
http://www-rocq.inria.fr/scilab/</a></dd>
<dt><strong><a name="item_Tela_1%2E32">Tela 1.32</a></strong><br>
</dt>
<dd><a href="http://www.geo.fmi.fi/prog/tela.html">
http://www.geo.fmi.fi/prog/tela.html</a></dd>
</dl>
<p>To find out about more numerical workbenches, check out <a href=
"http://sal.kachinatech.com/A/2/">http://sal.kachinatech.com/A/2/</a></p>
<h2><a name="introduction">Introduction</a></h2>
<p>What can these programs do? Isn't paper and pencil -- er -- a
spreadsheet program enough?</p>
<p>The main application areas of numerical workbenches are:</p>
<ul>
<li>Preprocessing (yes -- meanwhile we need computers to talk to
computers) and postprocessing data with the special case of ``gluing'' two
numerical applications together.</li>
<li>Numerical optimizations (both, linear and non-linear),</li>
<li>Standalone simulations,</li>
<li>Data visualization,</li>
<li>Sophisticated ``pocket'' calculators,</li>
<li>Rapid prototyping of specialized numerical applications, which finally
will be implemented in, say, C++ or Fortran-90.</li>
</ul>
<p>However, because all of them provide complete programming languages to the
user and, moreover, are designed to be extended, the number of numerical
problems they can solve is almost limitless.</p>
<h3><a name="numerical mathematics">Numerical Mathematics</a></h3>
<p>Now, what the heck is numerical math anyhow? Numerical Mathematics is the
branch of math that develops, analyzes, and applies methods to compute with
finite precision numbers. Computer hardware, for example, uses numerical math.
</p>
<p>Why do computers work with finite precision numbers? Why has nobody
developed a scheme that allows for the storage of exact numbers?</p>
<ol>
<li>Solving problems with finite precision numbers is faster -- much, much
faster. Take for example the sum of all square roots from one to one million.
On my computer, doing the exact computation with MuPAD-1.4.2 available at <a
href="http://math-www.uni-paderborn.de/MuPAD/index.html">
http://math-www.uni-paderborn.de/MuPAD/index.html</a>
<pre>
time(sum(sqrt(i), i = 1..10^6));
</pre>
<p>takes about 40&nbsp;seconds, whereas getting the approximate result with
Tela-1.32</p>
<pre>
tic(); sum(sqrt(1:10^6)); toc();
</pre>
<p>takes 0.31&nbsp;seconds, that is, the answer in finite precision is
returned over 100&nbsp;times faster! Put another way, we can crunch hundred
times more data with finite precision numbers in the same time slice.</p>
</li>
<li>When using good algorithms -- the ones suggested by numerical
mathematicians -- and being careful one can get surprisingly precise
answers even with finite precision numbers.<p></p></li>
<li>Admit it, most of the time users do not need exact results! A good
approximation -- with ``sufficiently many correct digits'' -- will
do.<p></p></li>
</ol>
<h3><a name="article organization">Article Organization</a></h3>
<p>In this article series, we point out the similarities among the three
applications that we are going to discuss. We will use GNU/Octave in most of
the examples. Where there are important differences you should be aware of, we
have put a <strong>Differences</strong> paragraph at the end of the section.</p>
<p>Technical details for the terminally curious have been put in <strong>
Details</strong> sections.</p>
<h2><a name="getting in and out">Getting In and Out</a></h2>
<p>To give you a hands-on experience, let us start each of the applications,
request help on a function, and then quit.</p>
<dl>
<dt><strong><a name="item_GNU%2FOctave">GNU/Octave</a></strong><br>
</dt>
<dd>
<pre>
cspiel@hydra:~/articles/numerical-workbenches $ octave
GNU Octave, version 2.1.34 (i686-pc-linux-gnu).
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 John W. Eaton.
This is free software with ABSOLUTELY NO WARRANTY.
For details, type `warranty'.
</pre>
<pre>
*** This is a development version of Octave. Development releases
*** are provided for people who want to help test, debug, and improve
*** Octave.
***
*** If you want a stable, well-tested version of Octave, you should be
*** using one of the stable releases (when this development release
*** was made, the latest stable version was 2.0.16).
</pre>
<pre>
octave:1&gt; help diag
diag is a built-in function
</pre>
<pre>
- Built-in Function: diag (V, K)
Return a diagonal matrix with vector V on diagonal K. The second
argument is optional. If it is positive, the vector is placed on
the K-th super-diagonal. If it is negative, it is placed on the
-K-th sub-diagonal. The default value of K is 0, and the vector
is placed on the main diagonal. For example,
</pre>
<pre>
diag ([1, 2, 3], 1)
=&gt; 0 1 0 0
0 0 2 0
0 0 0 3
0 0 0 0
</pre>
<pre>
octave:2&gt; quit
cspiel@hydra:~/articles/numerical-workbenches $
</pre>
<p>Alternatively use <code>exit</code> or press <strong>C-d</strong> to quit
GNU/Octave.</p>
<p>GNU/Octave offers the user function-name completion, this is, when only
part of a function's name is entered and the user hits <strong>Tab</strong>,
the partial name is completed as much as possible. A second <strong>
Tab</strong> displays the list of remaining choices.</p>
</dd>
<dt><strong><a name="item_Scilab">Scilab</a></strong><br>
</dt>
<dd>After starting Scilab thus:
<pre>
cspiel@hydra:~/articles/numerical-workbenches $ scilab
</pre>
<p>we get a new <a href="misc/spiel/scilab-main.png">X-window</a> in which the
Scilab interpreter runs. Asking for help opens an
<a href="misc/spiel/scilab-xless-help.png">xless(1x) window</a>.
(Both these links are screenshots, so click on them.)</p>
<p>To exit Scilab, enter <code>quit</code> or <code>exit</code>.</p>
<p>Scilab can also be launched in non-window mode by passing the <code>
-nw</code> option to it:</p>
<pre>
cspiel@hydra:~/articles/numerical-workbenches $ scilab -nw
===========
S c i l a b
===========
</pre>
<pre>
scilab-2.6
Copyright (C) 1989-2001 INRIA
</pre>
<pre>
Startup execution:
loading initial environment
</pre>
<pre>
--&gt;help diag
</pre>
<p>The help system then uses the text output, too.</p>
</dd>
<dt><strong><a name="item_Tela">Tela</a></strong><br>
</dt>
<dd>Tela's banner is quite terse, nonetheless, the help system is as
comprehensive as necessary. Note that Tela offers function name completion as
GNU/Octave does.
<pre>
cspiel@hydra:~/articles/numerical-workbenches $ tela
This tela is a tensor language, Version 1.32.
Type ?help for help.
-&gt;TAB completion works; try docview() and source("demo")
&gt;help diag
diag(V, K) (V is a vector) returns a square diagonal matrix, with
vector V on the main diagonal (K == 0, default), the K-th super
diagonal (K &gt; 0) or the K-th sub-diagonal (K &lt; 0).
</pre>
<pre>
diag(M, K) (M is a matrix) returns the main diagonal (K == 0,
default), the K-th super diagonal (K &gt; 0), or the K-th sub-diagonal
(K &lt; 0) of M as a vector. M need not necessarily be square.
</pre>
<pre>
&gt;quit()
63 instructions, 0 arithmetic ops.
0.0063 MIPS, 0 MFLOPS.
cspiel@hydra:~/articles/numerical-workbenches $
</pre>
<p>Tela can also be exited by pressing <strong>C-d</strong>.</p>
</dd>
</dl>
<h2><a name="better than a pocketcalculator!">Better Than a
Pocket Calculator!</a></h2>
<p>Now that we know how to start and exit the programs, let us look at them in
action.</p>
<h3><a name="simple expressions">Simple Expressions</a></h3>
<p>We want to see:</p>
<ol>
<li>Whether we can write mathematical expressions the way we are used to from
school. Ugh!
<BR><code>1&nbsp;+&nbsp;2&nbsp;*&nbsp;3&nbsp;^&nbsp;4</code> should be treated
as <code>1&nbsp;+&nbsp;(2 *&nbsp;(3&nbsp;^&nbsp;4))</code>, yielding
<code>163</code>. It should not be treated as
<code>((1&nbsp;+&nbsp;2)&nbsp;*&nbsp;3)&nbsp;^&nbsp;4</code>, which equals
<code>6561</code>,</li>
<li>How many bits are necessary to store <code>10^6</code>, and</li>
<li>How steep is our driveway? (measured in degrees)
Our garage is 7&nbsp;meters away from the street and half a meter above
it.</p>
</li>
</ol>
<p>Here we go.</p>
<pre>
cspiel@orion:~/articles/numerics $ octave
</pre>
<p>All three programs are console-based. That is, the user gets a prompt
whenever the application is ready to accept input. We enter our first question
as we write it on paper. Hitting return terminates the line, the program
evaluates it, and returns the result in variable&nbsp;<code>ans</code> (more on
variables later).</p>
<pre>
octave:1&gt; 1 + 2 * 3 ^ 4
ans = 163
</pre>
<p>Aha, obviously GNU/Octave knows elementary-school math!</p>
<p>Our second question requires the logarithm function&nbsp;<code>log</code>,
which returns the natural logarithm of its argument; this is, the logarithm to
base&nbsp;<em>e</em>.</p>
<pre>
octave:2&gt; log(10^6) / log(2)
ans = 19.932
</pre>
<p>We conclude that 1,000,000 needs 20&nbsp;bits to be stored.</p>
<p>Finally, how steep is our driveway? What we need here is an angular
function, namely the arctangent, written as <code>atan(argument)</code>.</p>
<pre>
octave:3&gt; atan(0.50 / 7.0)
ans = 0.071307
</pre>
<p>Hmm, ain't that a bit too flat? Digging out the wisdom of long forgotten
math classes, we remember that the arctangent of 1 is 45 degrees. Let us check
this!</p>
<pre>
octave:4&gt; atan(1)
ans = 0.78540
</pre>
<p>Ouch, off by a factor of 57! Do we have to throw the program away? Wait
-- 57 equals almost 180&nbsp;over&nbsp;<em>pi</em>. This means GNU/Octave
has returned the result in radians, not in degrees. All angular functions work
in units of radians, this is, an angle of 360 degrees is equivalent
2&nbsp;<em>pi</em>&nbsp;radians.</p>
<p>We try again, supplying the correct conversion factor:</p>
<pre>
octave:5&gt; atan(0.50 / 7.0) * 360/(2 * 3.14)
ans = 4.0856
</pre>
<p>Approximately 4&nbsp;degrees, that looks good. Our garage certainly won't
get flooded in the next deluge.</p>
<p><strong>Details</strong></p>
<ul>
<li>Numbers are either real-valued or complex-valued. Elementary operations
``+'', ``-'', ``*'', and ``/'', as well as exponentiation&nbsp;``^'' work as
expected on reals and complex numbers.</li>
<li>The commonly used basic functions are the absolute value <code>
abs(arg)</code>, the sign-function <code>sign(arg)</code>, and the square root
<code>sqrt(arg)</code>.</li>
<li>Two logarithm functions are supplied, one to the base of <em>e</em>:
<code>log(arg)</code>, and to the base of 10: <code>log10(arg)</code>. The
exponential <code>exp(arg)</code> is the inverse of <code>
log(arg)</code>.</li>
<li>All workbenches offer a wealth of angular and hyperbolic functions: <code>
sin(arg)</code>, <code>cos(arg)</code>, <code>tan(arg)</code>, <code>
sec(arg)</code>, <code>csc(arg)</code>; <code>asin(arg)</code>, <code>
acos(arg)</code>, <code>atan(arg)</code>, <code>acsc(arg)</code>; <code>
sinh(arg)</code>, <code>cosh(arg)</code>, <code>tanh(arg)</code>, <code>
sech(arg)</code>, <code>csch(arg)</code> <code>asinh(arg)</code>, <code>
acosh(arg)</code>, <code>atanh(arg)</code>, <code>acsch(arg)</code>.</li>
</ul>
<p><strong>Differences</strong></p>
<ul>
<li>Tela does not know the convenience variable <code>ans</code>.</li>
<li>In GNU/Octave and Tela imaginary literal numbers are written by appending
``i'', ``j'', ``I'', or ``J'' to a number. For example <code>1i</code>, <code>
-8.99I</code>, <code>324J</code>. Scilab defines a special constant for the
imaginary unit <code>sqrt(-1)</code>, which is written &lt;%i&gt;. Therefore,
Scilab's imaginary literals look like products: <code>-8.99*%i</code>, <code>
%i*324</code>.</li>
</ul>
<h3><a name="variables">Variables</a></h3>
<p>In the last section we have not gained much in comparison with a pocket
calculator, have we? The first feature where our programs beat pocket
calculators and spread-sheets are names that we can give parameters or
results; these are called variables.</p>
<p>Assume our better half wants us to build a garden in the yard, but we want
to watch basketball. Therefore we quickly need a hard figure that proves we
don't have enough compost for the desired size. Ha -- brilliant
idea!</p>
<img height="281" alt="[image: plan for a flower bed]" src=
"misc/spiel/patch.png" width="551">
<BR CLEAR="all">
<p>From our little plan we take the following lengths in feet:</p>
<pre>
houseside_length = 10
creekside_length = 6
width = 2
</pre>
<p>Our better half also said the layer of new soil ought to be at least
five inches, so</p>
<pre>
height = 5 / 12
</pre>
<p>GNU/Octave to the rescue!</p>
<pre>
octave:1&gt; houseside_length = 10
houseside_length = 10
octave:2&gt; creekside_length = 6
creekside_length = 6
octave:3&gt; width = 2
width = 2
octave:4&gt; height = 5 / 12
height = 0.41667
octave:5&gt; volume = (houseside_length + creekside_length) * width * height
volume = 13.333
</pre>
<p>The compost box is 6' x 4' and currently holds eight inches of usable
compost.</p>
<pre>
octave:6&gt; box_width = 6
box_wight = 6
octave:7&gt; box_depth = 4
box_depth = 4
octave:8&gt; compost_height = 8/12
compost_height = 0.66667
octave:9&gt; compost_volume = box_width * box_depth * compost_height
compost_volume = 16
</pre>
<p>Oh no, we have just dug our own grave. We have got <em>enough</em> compost!
What about taping the match on the VCR?</p>
<p><strong>Details</strong></p>
<ul>
<li>Variables spring into existence when they are first assigned to.</li>
<li>It is not an error to assign a value of different type to an existing
variable. (Currently, we only know of one type, but more types are waiting for
us)</li>
<li>Using a undefined variable on the right-hand side of an assignment causes
an error.</li>
</ul>
<h2><a name="structured data">Structured Data</a></h2>
<p>Until now we have not exploited where computers are really good at:
repetitive work.</p>
<h3><a name="vectors">Vectors</a></h3>
<p>Say we got a long receipt from the grocery store. [Your ad here!] How can
we get the VAT in Dollars on each item given the gross amount and the VAT rate
in percent? The formula</p>
<pre>
vat_percent / 100
vat = --------------------- * gross_amount
1 + vat_percent / 100
</pre>
<p>is trivial, but we want to save us repeated typing.</p>
<p>The list of all gross amounts in the receipt forms what numerical programs
call a <em>vector</em>. Vectors are built from values by enclosing these
values in square brackets and separating them with commas like this:</p>
<pre>
octave:1&gt; gross = [1.49, 4.98, 0.79, 5.49, 0.96, 0.96, 0.96, 0.96]
gross =
</pre>
<pre>
1.49000 4.98000 0.79000 5.49000 0.96000 0.96000 0.96000 0.96000
</pre>
<p>The vector is built from left to right using our supplied numbers in the
same order that we enter them.</p>
<p>Wouldn't it be wonderful if we simply wrote: <code>gross *
(vat_percent/100) / (1 + vat_percent/100)</code> and get the VAT of each item?
It really is that simple.</p>
<pre>
octave:2&gt; vat_percent = 7
vat_percent = 7
octave:3&gt; a = (vat_percent/100) / (1 + vat_percent/100)
a = 0.065421
octave:4&gt; vat = a * gross
vat =
</pre>
<pre>
0.097477 0.325794 0.051682 0.359159 0.062804 0.062804 0.062804 0.062804
</pre>
<p>Wow -- it works! For the first time we have really gained convenience
and expressiveness: a single multiplication sign performs eight
multiplications in a row.</p>
<p>What has happened? <code>vat_percent</code> is a single value, which is
called <em>scalar</em> in numerics to distinguish it from vectors. Well, if
<code>vat_percent</code> is a scalar, then <code>vat_percent/100</code>,
<code>1 + vat_percent/100</code>, and <code>a</code> are scalars, too.
Finally, scalar&nbsp;<code>a</code> must be multiplied with vector&nbsp;<code>
gross</code>. What we wanted and what happened was that <code>a</code> was
multiplied in turn with every element of <code>gross</code>. This holds for
every operator, not only multiplication! In general</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>vector</em>&nbsp;<em>
op</em>&nbsp;&nbsp;<em>scalar</em></p>
<p>and</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>scalar</em>&nbsp;<em>
op</em>&nbsp;&nbsp;<em>vector</em></p>
<p>apply <em>scalar</em> to every element in <em>vector</em> according to
operation <em>op</em>. In our example, this is as if we had written the
following</p>
<pre>
vat(1) = a * gross(1)
vat(2) = a * gross(2)
...
vat(8) = a * gross(8)
</pre>
<p>where we have introduced a new piece of syntax: vector indexing. Each
element (a scalar) of a vector can be accessed by its <em>index</em>, which is
the number of it's place in the vector. The index is written in parenthesis
after the vector. For example, to get the second element in <code>
gross</code>, we write</p>
<pre>
octave:5&gt; gross(2)
ans = 4.9800
</pre>
<p>Elements in vectors can be assigned to with the same syntax. Just place the
indexed vector to the left of the assignment sign, for example, <code>gross(2)
= 5.12</code>.</p>
<p>What else can be thought of as a vector of numbers besides our receipt? Any
series of values! Most of the time the values will be related, like the
temperature measured each day at 8am, the diameters of a batch of metal rods,
the velocities of all westbound traffic across Buffalo Street at the corner of
West Clinton Street on Wednesday April&nbsp;18, 2001. As we are living in the
Digital Age, many more series of data fit the notion of a vector: every piece
of music on a CD is a vector of sound amplitudes and the indices mark discrete
moments in time.</p>
<p><strong>Details</strong></p>
<ul>
<li>Mathematically speaking what we call vectors here are tuples.</li>
<li>Most built-in functions, for example <code>sin</code>, can be used on
vectors.
<pre>
v = [0.12, 0.89, 0.78, 0.10]
sin(v)
</pre>
<p>or</p>
<pre>
sin([0.12, 0.89, 0.78, 0.10])
</pre>
</li>
<li>Vectors do not have to be built from scalars; more than one vector can be
catenated with the same syntax. Furthermore, the elements used in the
definition of the vector do not have to be literal numbers, but can be
arbitrary expressions, which again yield scalars or vectors.</li>
</ul>
<p><strong>Differences</strong></p>
<ul>
<li>Tela uses a different syntax for vector construction, which resembles
Scheme or Smalltalk: The vector is surrounded by parentheses which are
preceded by a sharp sign; for example: <code>gross = #(1.49, 4.98, ...,
0.96)</code>.</li>
<li>Tela uses a different syntax for vector indexing, which resembles Pascal.
The index is enclosed in square brackets; for example: <code>gross[2]</code>.
Warning for C programmers: Though the square brackets look like C, the lowest
index always is 1.</li>
</ul>
<h2>Next Month</h2>
<ul>
<li>Matrices</li>
<li>User defined functions</li>
<li>Flow control statements</li>
<li>Input and output</li>
</ul>
<!-- *** BEGIN bio *** -->
<SPACER TYPE="vertical" SIZE="30">
<P>
<H4><IMG ALIGN=BOTTOM ALT="" SRC="../gx/note.gif">Christoph Spiel</H4>
<EM>Chris runs an Open Source Software consulting company in Upper Bavaria/Germany.
Despite being trained as a physicist -- he holds a PhD in physics from Munich
University of Technology -- his main interests revolve around numerics,
heterogenous programming environments, and software engineering. He can be
reached at
<A
HREF="mailto:cspiel@hammersmith-consulting.com">cspiel@hammersmith-consulting.com</A>.</EM>
<!-- *** END bio *** -->
<!-- *** BEGIN copyright *** -->
<P> <hr> <!-- P -->
<H5 ALIGN=center>
Copyright &copy; 2001, Christoph Spiel.<BR>
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR>
Published in Issue 69 of <i>Linux Gazette</i>, August 2001</H5>
<!-- *** END copyright *** -->
<!--startcut ==========================================================-->
<HR><P>
<CENTER>
<!-- *** BEGIN navbar *** -->
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="qubism.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue69/spiel.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="lg_backpage69.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
<!-- *** END navbar *** -->
</CENTER>
</BODY></HTML>
<!--endcut ============================================================-->