       OOOOOO                            VV        VV
      OO    OO   PPPPPPP   TTTTTTTT  II   VV      VV  EEEEEE   CCCCCC
      OO    OO   PP    PP     TT     II    VV    VV   EE       CC
      OO    OO   PPPPPPP      TT     II     VV   VV   EEEEEE   CC
      OO    OO   PP           TT     II       VVV     EE       CC
       OOOOOO    PP           TT     II        V      EEEEEE   CCCCCC


                       OptiVec  Version 1.5
                  for Borland Pascal and Delphi
            (Borland (Turbo) Pascal 7.0, Delphi 2, 3, 4)

                      Dr. Martin Sander Software Development
                      Serturnerstr. 11
                      D-37085 Goettingen
                      Germany
                      e-mail: MartinSander@Bigfoot.com
                      http://www.optivec.com

For the commercial version, see chapter 1.3.


*****************************************************************************

F i r s t  P a r t :   File  HANDBOOK.TXT

!!     This is an ASCII text file!  It is best viewed with a simple        !!
!!     DOS editor.                                                         !!
!!     If you load this file into a word processor under Windows, you      !!
!!     must use the filter "DOS text".                                     !!
!!     Alternatively, you may use FCONVERT (shipped with Borland Pascal)   !!
!!     to convert from ASCII (OEM) into the ANSI character set.            !!
!!     preferably use the lettertype CourierNew 10 pt.                     !!

Brand and product names mentioned in this handbook for identification
purposes are trademarks or registered trademarks of their respective holders.


****************************************************************************
*                                                                          *
*******                           Contents                           *******
*                                                                          *
****************************************************************************

F i r s t  P a r t :   File  HANDBOOK.TXT

This HANDBOOK describes the main part of the OptiVec package, which
is VectorLib. The other parts, CMATH and MatrixLib, have their own
descriptions in separate files.
CMATH:     see CMATH.TXT.
MatrixLib: not yet available for Pascal/Delphi.

1. Introduction
    1.1 What is VectorLib and Why are the VectorLib Functions so Fast?
    1.2 Licence Terms
    1.3 Registered Version
    1.4 Getting Started

2. The Elements of VectorLib Routines
    2.1 VectorLib Synonyms for some Pascal Data Types
    2.2 Complex Numbers: The Data Types fComplex,  dComplex,  eComplex
    2.3 Vectors and Arrays: The Data Types fVector, dVector, eVector,
        cfVector,  cdVector,  ceVector,   siVector, iVector, liVector,
        qiVector, usVector,  uVector,  and ulVector
    2.4 Real-Number Functions: The Prefixes VF_,  VD_,  and VE_
    2.5 Complex-Number Functions: The Prefixes VCF_, VCD_, and VCE_
    2.6 Functions of the Integer Data Types: The Prefixes VI_, VSI_,
        VLI_, VQI_, VU_, VUS_, and VUL_
    2.7 Common Functions of Several Data Types: The Prefix V_

3. The Environment
    3.1 The Different Versions of VectorLib: Selection of Target and Processor

4. VectorLib Functions and Routines: A Short Overview
    4.1 Generation, Initialization and De-Allocation of Vectors
    4.2 Index-oriented Manipulations
    4.3 Data-Type Interconversions
    4.4 More about Integer Arithmetics
    4.5 Basic Functions of Complex Vectors
    4.6 Mathematical Functions
        4.6.1 Rounding
        4.6.2 Comparisons
        4.6.3 Direct Bit-Manipulation
        4.6.4 Basic Arithmetics, Accumulations
        4.6.5 Powers
        4.6.6 Exponentials and Hyperbolic Functions
        4.6.7 Logarithms
        4.6.8 Trigonometric Functions
    4.7 Analysis
    4.8 Signal Processing:Fourier Transforms and Related Topics
    4.9 Statistical Functions and Building Blocks
    4.10 Input and Output
    4.11 Graphics

5. Error Handling
    5.1 General Remarks
    5.2 Integer Errors
    5.3 Floating-Point Errors
    5.4 The Treatment of Denormal Numbers
    5.5 Advanced Error Handling: Writing Messages into a File

6. Trouble-Shooting
    6.1 General Problems
    6.2 Problems with Windows?

7. The Units of VectorLib


S e c o n d  P a r t :  File  FUNCREF.TXT

 8.  Alphabetical Reference

 9.  Non-vectorized Functions

10. VectorLib Error Messages



****************************************************************************
*                                                                          *
*******                       1. Introduction                        *******
*                                                                          *
****************************************************************************


1.1 What is VectorLib and Why are the VectorLib Functions so Fast?
------------------------------------------------------------------

VectorLib offers a powerful library of routines for numerically demanding
applications, making the philosophy of vectorized programming available for
C/C++, Pascal, and Fortran languages. VectorLib serves to overcome the
limitations of loop management of conventional compilers - which proved to
be one of the largest obstacles in the programmer's way towards efficient
coding for scientific and data analysis applications.

Conventionally, a vector, i.e. a one-dimensional array of data of the same
type, would be processed by "dissolving" it into a loop over its elements,
leaving it to the compiler to produce efficient code. Compiled code, however,
is always far from perfect. This means that your computer is occupied with
slow and often inaccurate calculations. Now, with VectorLib, things become
easier: vectors are processed as a whole; they need no longer be dissolved
into loops. A large set of strictly typed functions is defined and realized
in a tight Assembler-written implementation.

In comparison to the old vector language APL, VectorLib has the advantage of
being incorporated into the modern and versatile languages C/C++, Pascal, and
Fortran. Recent versions of C++ and Fortran do already offer some sort of
vector processing, by virtue of iterator classes using templates (C++) and
field functions (Fortran90). Both of these, however, are basically a con-
venient means of letting the compiler write the loop for you and then compile
it to the usual inefficient code. The same is true for the popular BLAS
(Basic Linear Array Subroutine) libraries for Fortran. In comparison to
these approaches, VectorLib is superior mainly with respect to execution
speed - on the average by a factor of 2-3, in some cases even up to 8.
The performance is no longer limited by the quality of your compiler, but
rather by the real speed of the processor!

Moreover, the input and output vectors of VectorLib routines may be of
variable size and it is possible to process only a part (e.g., the first 100
elements, or every 10th element) of a vector, which is another important
advantage of the VectorLib functions over other approaches, where only whole
arrays are processed.

Using VectorLib routines instead of loops can make your source code much more
compact and far better readable.

Besides this increased efficiency and ease of programming, the wide range of
routines and functions covered by VectorLib makes this package the preferrable
programming tool for scientific and data analysis applications, competing with
many high-priced integrated systems, but imbedded into your favourite
programming language:

*   All operators and mathematical functions of Pascal are implemented in
    vectorized form; additionally many more mathematical functions are
    included which normally would have to be calculated by more or less
    complicated combinations of existing functions. Not only the execution
    speed, but also the accuracy of the results is greatly improved.

*   Building blocks for statistical data analysis are supplied.

*   Derivatives, integrals, interpolation schemes are included.

*   Fast Fourier Transform techniques allow for efficient convolutions,
    correlation analyses, spectral filtering, and so on.

*   Graphical representation of data offers a convenient way of monitoring
    the results of vectorized calculations.

*   Each function exists for every data type for which this is reasonable.
    The data type is signalled by the prefix of the function name.
    As far as allowed by the rules of the various languages, the names and
    the syntax of nearly all functions are the same in C/C++, Pascal and
    Fortran.

*   Besides the vectorized complex functions, CMATH is included, offering
    (non-vectorized) complex-number arithmetics and mathematics.

*   A large set of matrix operations is provided by MatrixLib, which will
    be included in the next update of the OptiVec package.


As noted above, all functions, except some of the graphics and I/O routines,
are written in Assembly language. This made optimizations possible which are
not available in code produced by a compiler. You need not know any of the
technical details described in the following lines and you may skip them,
but perhaps these explanations will give you an idea of which performance
to expect from VectorLib.

*   Floating-point constants, which are needed in the evaluation of
    mathematical functions, are left on the floating-point number stack as
    long as they are needed. This saves a large amount of loading/unloading
    operations which are necessary if a mathematical function is called for
    each element of a vector separately.

*   Where necessary, all eight coprocessor registers are employed. (For
    present compilers, it is already an excellent achievement to master the
    book-keeping for only four coprocessor registers.)

*   The addressing of vector elements is still a major source of inefficiency
    with present compilers. Swtiching forth and back between input and output
    vectors, a large number of redundant addressing operations is performed.
    The strict (and easy!) definitions of all VectorLib functions allow to
    reduce these operations to a minimum.

*   The 32-bit operations of the 386+ and the improved coprocessor commands
    of the 387+ are consequently used. (This is why the present Shareware
    version does not run on 286 machines and not without coprocessor.)

*  For any operations with floating-point numbers that can also be performed
   using integer commands (like copying, swapping, or pushing onto the stack),
   the faster method is consistently employed.

*  Pascal compilers convert Singles and Doubles into Extended, before passing
   them to a mathematical function. This approach was useful at times when
   disk memory was too great a problem to include separate functions for each
   data type, but it is simply inefficient on modern PCs.
   Consequently, no such implicit conversions are present in the VectorLib
   routines. Here, a function of a Single is calculated to Single precision,
   wasting no time for the calculation of more digits than necessary - which
   would be discarded anyway.

*  All external function calls are eliminated from the inner loops of the
   vector processing. This saves the execution time necessary for the
   "call / ret" pairs and for loading the parameters on the stack.

*  To ensure proper parallel processing of the CPU and the floating-point
   unit, compilers have to be careful with the synchronization, adding a
   lot of "wait" statements to the program code. Within the tightly defined
   VectorLib routines, wait commands are used only where it is really
   necessary (not relevant for the Pentium processor: no "wait" needed
   at all).

*  In many cases (like the fast integer addition in VI_addC), the
   vectors are processed in chunks of up to four elements instead of single
   ones, reducing the relative amount of time spent for loop management.

*  In addition to these vector-specific optimizations, VectorLib employs all
   optimizations you expect from good compilers, including  organization of
   the code for most efficient use with superscalar processors like the
   Pentium and its competitors.

The implementation described in this handbook refers to Borland (Turbo)
Pascal, version 7.0 or later for DOS and Microsoft Windows 3.0 or later
and Borland Delphi under Windows 3.x (16-bit version) or Windows95 / NT
(32-bit version).
Presently available Shareware versions are designed for Borland Pascal
(real-mode DOS and Windows 3.x) and 16-bit Delphi.
At least, a 386 computer equipped with a 387 coprocessor is required (this
means: no emulation, no 486SX; but preferrably 486DX, Pentium or higher).

The registered version contains OptiVec units for all targets of
Borland Pascal (DOS real and protected mode, Windows 3.x) and Delphi
(both 16-bit and 32-bit versions).
For each of these targets, OptiVec units are shipped in three versions:
one for 486DX or Pentium computers, the second for 386 with 387, the third
for 286 with or without coprocessor, i.e. with emulation.

OptiVec is currently available for Borland Pascal, Borland C++, and
Microsoft Visual C++.
Versions for Delphi and other compilers are in preparation.

For two-dimensional arrays, MatrixLib is available for the C/C++ versions
of OptiVec, but not yet for the Pascal/Delphi ones.

TensorLib is planned as a future extension of these concepts for general
multidimensional arrays.


1.2 Licence Terms
-----------------

This is the English Shareware version of OptiVec for Borland Pascal/Delphi
("SOFTWARE"). It may be used under the following licence terms:

1. You may test the SOFTWARE free of charge for an unlimited period of time.
   This testing phase ends when you permanently integrate functions of this
   SOFTWARE into any of your applications (programs, program parts...).
2. If you want to use this SOFTWARE for commercial purposes, you have
   to purchase the registered version (see chapter 1.3).
3. Use of this SOFTWARE for educational purposes at schools and universities
   remains free of charge. However, if any application created under these
   terms is sold to others or otherwise used for commercial purposes,
   paragraph 2 applies.
4. Distributing this SOFTWARE to others is allowed only in one of the
   following two ways:
   a) linked into your programs, so that the parts stemming from this
      SOFTWARE do no longer appear as a library.
   b) as a whole in unchanged form (in particular the Copyright and Licence
      statements!), whereby you may ask a fee only and exclusively for the
      physical act of copying the SOFTWARE.
5. This SOFTWARE is provided on an "as is" basis. Any explicit or implicit
   warranties for the SOFTWARE are excluded.
   Despite thorough testing of the SOFTWARE, errors and bugs cannot
   be excluded with certainty. No claims as to merchantability or fitness
   for a particular purpose are made.
   You may not use the SOFTWARE in any environment or situation where
   personal injury or excessive damage to anyone's property (including
   your own) could arise from malfunctioning of the SOFTWARE.


Copyright for the SOFTWARE and its documentation (C) 1996-1999 Martin Sander

All rights reserved, including those of translation into foreign languages.

Address of the author:
              Dr. Martin Sander Software Development
              Sertrnerstr. 11
              D-37085 Gttingen
              Germany
              e-mail: MartinSander@Bigfoot.com
              http://www.optivec.com


1.3 Registered Version
----------------------

The registered version of OptiVec for Borland Pascal

-  supports all targets of Borland Pascal 

-  has individually optimized libraries for each degree of
   backward-compatibility:
      486DX/Pentium+
      386+ (coprocessor required)
      286+ (no coprocessor required; the 286 version of course only in 16-bit)

-  costs USD 35  or  EUR  29  per unit,
         plus a mailing fee of  $5 or EUR 5.

   For ordering, please send a printout of ORDER.TXT with enclosed cheque
   to the author.

-  if you upgrade to OptiVec for Delphi after that version
   becomes available, the above registration fee will be deduced
   from the price of OptiVec for Delphi.

Purchasing the registered version gives you the right to use it
on one computer at a time. Multi-user licences are available upon request.


1.4 Getting Started
-------------------

*   In order to use OptiVec, you need an already installed copy of Borland
    Pascal or Delphi. Install OptiVec by executing INSTALL.EXE from the
    installation disk. Follow the instructions given by the installation
    program. Normally, a sub-directory "OPTIVEC" will be created to hold the
    VectorLib documentation. The units will be installed into the directory
    OPTIVEC\UNITS.

*   If you can, use the protected-mode version of the compiler, i.e.,
    BP.EXE or TPX.EXE.  The real-mode version, TP.EXE, might run out
    of link memory. If you encounter that problem, set the linker
    option "link buffer" to "disk". If even that does not help, you
    can only use the command-line compiler.

*   Include the OptiVec units directory into the units search path.

*   Borland Pascal only: Choose the target DOS real mode.

*   Declare the use of OptiVec units as usual with the "uses" statement.
    They are grouped according to the data type. See chapter 7.



****************************************************************************
*                                                                          *
*******               2. Elements of VectorLib Routines              *******
*                                                                          *
****************************************************************************

2.1 VectorLib Synonyms for some Pascal Data Types
-------------------------------------------------

We noted above that - as far as possible - all VectorLib routines shall have
identical names in Pascal, C/C++, and Fortran languages. Since the function
prefixes are derived from the data types of the processed vectors (see below),
this necessitates the definition of alias names for some data types denoted
differently in the various languages. You are not forced to use these alias
names anywhere, but we need them in order to build a consistent nomenclature
of VectorLib function names. If you like, you may use the synonyms yourself.
They are made available by defining them in the unit VecLib.

The data type Float, which is familiar to C/C++ programmers, is defined as a
synonym for Single. We prefer to have the letters defining the real-number
data types in alphabetical proximity: "D" for Double, "E" for Extended, and
"F" for Float. Maybe the future will bring high-precision 128-bit and 256-bit
real numbers which could find their place in this series as "G" for Great and
"H" for Hyper.

For historical reasons, the various integer data types have a somewhat
confusing nomenclature in Pascal. Trying to introduce a more systematic
nomenclature and to make the derived function prefixes compatible to
the C and Fortran versions of VectorLib, we define synonyms for several
types, as described in the following table:

Ŀ
   type              Pascal/Delphi name    synonym       derived prefix 
Ĵ
  8 bit signed        ShortInt              ByteInt        VBI_         
  8 bit unsigned      Byte                  UByte          VUB_         
 16 bit signed        SmallInt                             VSI_         
 16 bit unsigned      Word                  USmall         VUS_         
 32 bit signed        LongInt                              VLI_         
 32 bit unsigned      -----                 ULong          VUL_         
 64 bit signed        Comp                  QuadInt        VQI_         
 16/32 bit signed     Integer                              VI_          
 16/32 bit unsigned   Cardinal              UInt           VU_          


The unsigned 32-bit integer type ULong is unique to VectorLib. Outside
VectorLib routines, all ULong variables will be treated as LongInt. This
means that the most significant bit will then be interpreted as the sign bit,
which may lead to errors, unless proper care is taken to avoid mistakes.
QuadInts are always signed. There is nothing like a "UQuad".

To have a Boolean data type available which is of the same size as Integer,
we define the type IntBool to be WordBool in Borland Pascal and 16-bit Delphi,
but LongBool in 32-bit Delphi. You will see this data type as the return
value of many mathematical VectorLib functions.



2.2 Complex Numbers:
The Data Types fComplex,  dComplex,  eComplex
---------------------------------------------

VectorLib defines three complex data types. Their names are derived from the
types of the real numbers they consist of (with the data type Float being
defined as a synonym for Single, see above):
    type  fComplex = record   Re, Im: Float;     end;
    type  dComplex = record   Re, Im: Double;    end;
    type  eComplex = record   Re, Im: Extended;  end;

If, for example, a complex number z is declared as   "fComplex  z;", the
real and imaginary parts of z are available as z.Re and z.Im, resp.
Complex numbers are initialized by setting the real and imaginary parts
separately to the desired value, e.g.
	z.Re = 3.0;   z.Im = 5.7;

Alternatively, the function fcplx may be used as
       fcplx( z, 3.0, 5.7 ); 

Pointers to arrays or vectors of complex numbers are declared using the
data types cfVector, cdVector, and ceVector, described below.

VectorLib includes a large range of vectorized as well as "normal" (non-
vectorized) functions of these three complex data types. The non-vectorized
forms are contained in the CMATH library which is described in greater
detail in the file CMATH.TXT. VectorLib itself contains the necessary
initialization functions of complex numbers and all vectorized forms of
the complex operations supported.



2.3 Vectors and Arrays:
The Data Types  fVector,  dVector,  eVector,  cfVector,  cdVector,  ceVector,
                siVector, iVector,  liVector,  qiVector,
                usVector, uVector,  and ulVector
-----------------------------------------------------------------------------

We define, as usual, a "vector" as a one-dimensional array of data containing,
at least, one element, with all elements being of the same data type. Using a
more mathematical definition, a vector is a rank-one tensor. A two-dimensional
array (i.e. a rank-two tensor) is denoted as a "matrix", and higher
dimensions are always referred to as "tensors".

In contrast to other approaches, VectorLib does not allow zero-size vectors!

The basis of all VectorLib routines is formed by the various vector data types
given below and defined in the unit VecLib. In your programs, you may mix
these vector types with the static arrays of traditional Pascal style. When
passing a static Pascal array to a VectorLib function, you must use the
address-operator, whereas the VectorLib types are already defined as pointers.
For example:
   a:   array[0..99] of Single;     (* static array *)
   b:   fVector;           (* vector as defined by VectorLib *)
   VF_equ1( @a, 100 );      (* set the first 100 elements of a equal to 1.0  *)
   b  :=  VF_vector( 100 );
   VF_equC(  b, 100, 3.7 ); (* set the first 100 elements of b equal to 3.7  *)

In contrast to the fixed-size static arrays, the VectorLib types use dynamic
memory allocation and allow for varying sizes. Because of this increased
flexibility, we recommend that you predominantly use the latter. Here
they are:
   type   fVector   =  ^Float;
   type   dVector   =  ^Double;
   type   eVector   =  ^Extended;
   type   cfVector  =  ^fComplex;
   type   cdVector  =  ^dComplex;
   type   ceVector  =  ^eComplex;
   type   biVector  =  ^ByteInt;
   type   siVector  =  ^SmallInt;
   type   liVector  =  ^LongInt;
   type   qiVector  =  ^QuadInt;
   type   ubVector  =  ^UByte;
   type   usVector  =  ^USmall;
   type   ulVector  =  ^ULong;
   type   iVector   =  ^Integer;
   type   uVector   =  ^UInt;

Thus, internally, a data type like fVector means "pointer to Float", but you
may think of a variable declared as fVector  rather in terms of a "vector
of Floats". The data types Float, UInt, ULong, QuadInt, fComplex, dComplex,
and eComplex themselves are described above.

   Note: in connection with Windows programs, often the letter "l" or "L"
   is used to denote LongInt variables. In order to prevent confusion,
   however, the data type LongInt is signalled by "li" or "LI", and the
   data type ULong is signalled by "ul" or "UL".



2.4 Real-number Functions:
    The Prefixes VF_,  VD_,  and VE_
------------------------------------

TheVectorLib package supports the three floating-point data types that are
used by the coprocessors of the 80x87 family and the FPU units integrated into
the i486 and its successors:  Single,  Double, and Extended. Neither BCD
numbers nor the original Pascal type Real are supported. Any of the algebraic
and mathematical functions included in this library therefore exists in one
variant for each floating-point format. The data type of all floating-point
vector elements, parameters, and of the return value is always the same
within one function. The data type is signalled by the second letter of the
prefix: VF_ denotes the variant of a function that uses exclusively the data
type Float (= Single), VD_ stands for the data type Double, and VE_ for the
data type Extended. (The first letter, "V", stands for "Vector function",
of course.)  VF_ functions thus work on arrays declared as fVector, use
parameters of the type Float, and, if there is any floating-point return
value, this will also be of the type Float. There are no mixed-type functions
(that would, e.g., work on vectors of type fVector, use parameters of type
Double and return a value of type Extended).
One partial exception from this rule comes from the fact that, in Borland
Pascal, floating-point return values are always stored as Extendeds on the
number stack; therefore, you may assign the return value of a floating-
point function to a variable of another data type. For example, the product
of all elements of a vector may easily overflow, and it is a good idea to
define eProd as an Extended before writing the line
   eProd := VF_prod( X, size );  .

For the description of the functions and procedures in the Alphabetical
Reference (chapter 8), generally only the VF_ version is described and its
syntax explicitly given. The versions for the data types Double and
Extended are exactly analogous to the VF_ variant. You have only to
replace the prefix  "VF_" by  "VD_" (or "VE_") and to use  "dVector" and
"Double" (or "eVector" and "Extended", resp.) wherever you find "fVector"
and "Float" in the VF_ version.



2.5 Complex-number Functions:
    The Prefixes VCF_, VCD_, and VCE_
--------------------------------------

Any prefix with its second letter being "C" denotes a function of complex
numbers. By analogy with the nomenclature used for real-number functions, the
prefix VCF_ signals the exclusive use of single-precision vectors, parameters
and return values (fComplex, fVector and float). Similarly, VCD_ is used for
double-precision calculations, and VCE_ for extended precision. Wherever
"fComplex", "fVector", and "Float" appear in the description of a function in
the VCF_ version, the VCD_ and VCE_ versions are obtained by substituting with
"dComplex",  "dVector" and "Double"  or "eComplex", "eVector", and "Extended"
(or "long double"), resp.

Note: Return values of the data types fComplex, dComplex, and eComplex are
not possible in Turbo Pascal, but they are possible in C/C++. Therefore, the
syntax of those functions returning a complex number is different in C/C++
and Turbo Pascal.



2.6 Functions of the Integer Data Types:
    The Prefixes VI_, VSI_,  VLI_, VQI_, VU_, VUS_, and VUL_
------------------------------------------------------------

The nomenclature for the integer data types is designed in a similar way as
for the floating-point data types:
VI_ indicates the use of the data type Integer, VSI_ stands for SmallInt,
VLI_ stands for LongInt and VQI_ for QuadInt, VU_ denotes operations with
UInt, VUS_ works on unsigned 16-bit integers (USmall) and VUL_ is the prefix
for functions of ULong arguments.

If present, the vectorized integer functions are always described together
with their floating-point analogues. To obtain, for example, the VI_
version, vectors of type iVector have to be substituted for those of type
fVector which are demanded by the VF_ version. In the same way, the other
versions are obtained by changing "Float" (= Single) and "fVector" into the
desired data type.

Like the names, also the units in which the routines are contained are named
according to the data type they belong to. Thus, the functions and procedures
of the data type Integer are to be found in VIstd  and VImath, those of the
data type ULong in VULstd and VULmath, and so on.



2.7 Common Functions of Several Data Types:  The Prefix V_
----------------------------------------------------------

Several functions exist that are either used independently of any data type
or that are used to interconvert the data types. Functions like V_initPlot
and V_free belong to the first case (you have to initialize the plotting
routines regardless of the data type of the vectors you are going to plot,
and the initialization is not specific for any data type).
A function like V_ULtoD  belongs to the second case; here, a  ulVector
(a vector whose elements are of the data type ULong) is transformed into
a dVector (a vector whose elements are Doubles).
The type-independent functions are contained in the units VecLib and Vgraph.
The data-type interconversion functions are contained in the units belonging
to the destination type (i.e. the type into which the numbers are converted).


****************************************************************************
*                                                                          *
*******                      3. The Environment                      *******
*                                                                          *
****************************************************************************

3.1 The Different Versions of OptiVec:
    Selection of Target and Processor
---------------------------------------------------

At present, Shareware versions of OptiVec are available for
the targets DOS real-mode and Windows 3.x of Borland Pascal 7.0.
Units for the DOS protected-mode of Borland Pascal are available only
in the registered version. Versions for Delphi are in preparation.

If you followed the standard installation, add
C:\BP\OPTIVEC\UNITS      to the unit search path of Borland Pascal.

For units backwards-compatible with the 286 processor and for the most
advanced version of OptiVec, requiring a 486 or Pentium, you need the
registered version.



****************************************************************************
*                                                                          *
*******           4. VectorLib Functions and Routines:               *******
*******              A Short Overview                                *******
*                                                                          *
****************************************************************************


4.1 Generation, Initialization and De-Allocation of Vectors
-----------------------------------------------------------

With VectorLib, you may use static arrays like, for example,
    a: array[0..100] of Single;
as well as dynamically allocated ones (see chapter 2.3). We recommend,
however, that you use the more flexible vector types defined by VectorLib,
using dynamic allocation. This is described in the following sections.
After a vector has been declared (e.g., as  X: fVector; ), memory has to be
allocated. When the vector is no longer needed, the memory it occupies has
to be de-allocated again. For the allocation of memory, one specific function
exists for each data type:  VF_vector,   VD_vector,  VI_vector,  and so on.
If, together with the allocation, all elements shall be initialized with 0,
VF_vector0,  VD_vector0,  VI_vector0,   etc. may be called.  To de-allocate
memory, one and the same function is used for all data types:  V_free. In
order to de-allocate several vectors with only one call, use V_nfree.

Internally, the allocated vectors are written into a table to keep track
of the allocated memory. If you try to free a vector that has never been
or is no longer allocated, you get a warning message, and nothing is freed.

The following functions are used to initialize or re-initialize vectors that
have already been created:
VF_equ0   sets all elements of a vector equal to 0;
VF_equ1   sets them equal to 1;
VF_equC   sets them equal to a constant.
VF_equV   makes one vector a copy of another,
VFx_equV  (the "expanded" version of the equality operation) relates each
          element of a vector to the corresponding element of another
          according to the formula  Yi = a  Xi + b.

VF_ramp   fills a vector with a "ramp" according to the formula  Xi = ai + b.
VF_random fills a vector with high-quality random numbers,
VF_noise  with white noise, and
VF_comb   with a "comb" function which, at equidistant points, equals a
          constant C and is zero elsewhere.

VF_Hanning,  VF_Parzen, and VF_Welch are special functions creating so-called
windows for use in spectral analysis (see VF_spectrum).

Complex vectors may be initialized by assigning the real and imaginary parts
separately:  VF_ReImtoC,  VF_RetoC, and VF_ImtoC. Alternatively, they may be
formed out of polar coordinates: VF_PolartoC.



4.2 Index-oriented Manipulations
--------------------------------

VF_rev       is used to reverse the ordering of the elements of a vector.
VF_reflect   sets the upper half of a vector equal to the reversed lower half.
VF_rotate    is used to rotate the ordering of the elements.
VF_insert    and
VF_delete    insert or delete an element of a vector.

VF_sort      is used for fast sorting of the elements into ascending or
             descending order. If only an index-array, but not the elements
             themselves are to be rearranged,
VF_sortind   does the job.
VF_subvector extracts a subvector from a (normally larger) vector, using a
             constant sampling interval.
VF_indpick   fills a vector with elements "picked" from another vector
             according to their indices.
VF_indput    is the complement of VF_indpick and distributes the elements of
             one vector to the sites of another vector specified by their
             indices.

Operations performed only on a sampled sub-set of elements of a vector are
provided by the   VF_subvector_...  family, where the omission mark stands
for a suffix denoting the desired operation.

VF_searchC   searches for the element of a vector that is closest to a
             pre-set value (with a parameter mode deciding if the closest,
             the closest larger-or-equal, or the closest smaller-or-equal
             value is chosen).
VF_searchV   does the same for a whole array of pre-set values.

Polynomial, rational, and cubic-spline interpolations are performed by
VF_polyinterpol,  VF_ratinterpol, and VF_splineinterpol, resp.



4.3 Data-Type Interconversions
------------------------------

The first thing that has to be said about the floating-point data-type
interconversions is: do not use them too extensively. Decide which accuracy
is appropriate for your application, and then use consistently either the
VF_, or the VD_, or the VE_ version of the functions you need. Nevertheless,
every data type can be converted into every other, in case it is necessary.

The functions used for the interconversion of the real-value floating-point
data types are: V_FtoD,  V_DtoF,  V_FtoE,  V_EtoF,  V_DtoE,  and V_EtoD.
Similarly, the following functions are offered for the complex floating-
point data types: V_CFtoCD,  V_CDtoCF,  V_CFtoCE,  V_CEtoCF,  V_CDtoCE,  and
V_CEtoCD.

Corresponding to the large number of integer data types, there is an even
larger number of functions interconverting them. Switching between "normal",
short, long and "quadruple" integers is performed by V_ItoLI,  V_ItoQI,
V_ItoSI,  V_SItoI,  V_SItoLI,  V_SItoQI,  V_LItoSI,  V_LItoI,  V_LItoQI,
V_QItoSI,  V_QItoI,  and V_QItoLI.

Similarly, the available types of unsigned numbers are interconverted by
V_UtoUL,  V_UtoUS,  V_UStoU,  V_UStoUL,  V_ULtoUS,  and  V_ULtoU.

Interconversions between signed and unsigned types can only be performed on
the same level of accuracy, namely by the functions V_ItoU,  V_UtoI,
V_LItoUL, V_ULtoLI,  V_SItoUS,  and V_UStoSI.  That means that functions
like  V_UStoLI  do  n o t  exist!

The conversion of integer into floating-point types is accomplished by
V_ItoF,  V_ItoD,  V_ItoE,  V_SItoF,  V_SItoD,  V_SItoE,  V_LItoF,  V_LItoD,
V_LItoE,  V_QItoF,  V_QItoD,  V_QItoE,  V_UtoF,  V_UtoD,  V_UtoE,  V_UStoF,
V_UStoD,  V_UStoE,  V_ULtoF,  V_ULtoD,  and V_ULtoE. Again, do not be
confused by the large number of these functions, but keep only in mind that
for every interconversion there is one available.

The reverse process, the conversion of floating-point numbers into integers,
is more complicated: although every integer (except for extremely large
ones) has an exact representation in the floating-point types, this is not
true the other way round: floating-point numbers may by definition contain
fractional, i.e. "non-integer" parts. By choosing the appropriate rounding
function, the user has to decide how to treat these fractional parts:
Neglect them ("chop" or "trunc"), round to the nearest whole number ("round"),
round to the next greater-or-equal integer ("ceil") or to the next smaller-or-
equal integer ("floor"). These options are treated as mathematical functions
and are described in chapter 4.6.1.



4.4 More about Integer Arithmetics
----------------------------------

Although the rules of integer arithmetics are quite straightforward, it
appears appropriate to recall that all integer operations are implicitly
performed modulo 2**n, where n is the number of bits the numbers are
represented with. This means that any result, falling outside the range of
the respective data type, is made to fall inside the range by loosing the
highest bits. The effect is the same as if as many times 2**n had been added
to (or subtracted from) the "correct" result as necessary to reach the legal
range.
For example, in the data type SmallInt, the result of the multiplication
5 * 20000 is  -31072. The reason for this seemingly wrong negative result is
that the "correct" result, 100000, falls outside the range of SmallInt
numbers which is -32768 <= x <= +32767.  SmallInts are 16-bit numbers, so
n = 16, and 2**n = 65536. In order to make the result fall into the specified
range, the processor "subtracts" 2 * 65536 = 131072 from 100000, yielding
-31072.

Note that overflowing intermediate results cannot be "cured" by any following
operation. For example, (5 * 20000) / 4 is not (as one might hope) 25000,
but rather -7768.

Note furthermore that the 64-bit data type QuadInt does not employ this
implicit modulo 2**n-arithmetics. Overflow conditions lead to undefined
results.



4.5 Basic Functions of Complex Vectors
--------------------------------------

The following functions are available for the basic treatment of complex
vectors.
VF_ReImtoC      forming a complex vector out of its real and imaginary parts,
VF_RetoC        overwriting the real part,
VF_ImtoC        overwriting the imaginary part,
VF_CtoReIm      extracting the real and imaginary parts,
VF_CtoRe        extracting the real part,
VF_CtoIm        extracting the imaginary part,
VF_PolartoC     forming a complex vector out of polar coordinates,
VF_CtoPolar     transforming a complex vector into polar coordinates,
VF_CtoAbs       calculating the absolute value (the magnitude of the pointer
                in the complex plane),
VF_CtoArg       calculating the argument (the angle of the pointer in the
                complex plane),  and
VF_CtoNorm      calculating the norm (which is defined here as the square of
                the absolute value).



4.6 Mathematical Functions
--------------------------

Lacking a more well-founded definition, we denote as "mathematical" all
those functions which calculate each single element of a vector from the
corresponding element of another vector by a more or less simple
mathematical formula:  Yi = f( Xi ).   Except for the "basic arithmetics"
functions, they are defined only for the floating-point data types.
Most of these mathematical functions are vectorized versions of Pascal
functions or derived from them. Within VectorLib math functions, errors are
handled in a more flexible way than in Pascal/Delphi math functions: You can
decide yourself how to react on errors by calling the functions
V_setFPErrorHandling  and  V_setIntErrorHandling.
In addition to this error handling "by element", the return values of the
VectorLib math functions show if all elements have been processed success-
fully. If so, the return value is FALSE, otherwise it is TRUE.


4.6.1 Rounding
--------------

As noted in connection with data-type interconversions, the conversion of
floating-point numbers to integer data types may be accomplished by four
different ways: Fractional parts may be neglected (VF_chop, VF_trunc), or
the numbers may be rounded to the nearest integer (VF_round),  to the next
greater-or-equal integer (VF_ceil), or to the next smaller-or-equal integer
(VF_floor).

The result of the rounding operation thus specified may either be left in
the original floating-point format, e.g., in VF_round, or it may be converted
into one of the integer types, as in VF_roundtoI,  VD_ceiltoLI, VF_choptoSI,
or VE_floortoQI. As long as the input numbers are positive, they can also be
rounded to the unsigned integer types,  as in VF_floortoU, VF_ceiltoUS,
VD_choptoUL, or VE_trunctoUI.



4.6.2 Comparisons
-----------------

Functions performing comparisons are generally named VF_cmp... (where
further letters and/or numbers specify the type of comparison desired).
Every element of a vector can be compared either to 0, or to a constant C,
or to the corresponding element of another vector. There are two
possibilities: either the comparison is performed with the three possible
answers "greater than", "equal to" or "less than". In this case, the results
are stored as floating-point numbers (0.0,  1.0,  or -1.0). Examples are
VF_cmp0,  VD_cmpC,  and VE_cmpV.

The other possibility is to test if one of the following conditions is
fulfilled: "greater than", "greater than or equal to", "equal to", "not
equal to", "less than", or "less than or equal to". Here, the answers will
be "TRUE" or "FALSE" (1.0 or 0.0). Examples are VF_cmp_eq0,  VD_cmp_gtC, and
VE_cmp_leV.

Alternatively, the indices of the elements for which the answer was "TRUE"
may be stored in an index-array, as in VF_cmp_neCind, VD_cmp_lt0ind,  and
VE_cmp_geVind.

While the basic comparison functions check against one boundary, there is
a number of functions checking if a vector elements falls into a certain
range.
VF_cmp_inclrange0C   TRUE for 0 <= x <= C  (C positive),
                              0 >= x >= C  (C negative).
VF_cmp_exclrange0C   TRUE for 0 <  x <  C  (C positive),
                              0 >  x >  C  (C negative).
VF_cmp_inclrangeCC   TRUE for CLo <= x <= CHi,
VF_cmp_exclrangeCC   TRUE for CLo <  x <  CHi.

The variants of these functions that store the indices of elements yielding
"TRUE" are VF_cmp_inclrange0Cind, VF_cmp_exclrange0Cind,
VF_cmp_inclrangeCCind, and VF_cmp_exclrangeCCind.

To test if (at least) one element of a table is equal to a preset value, the
function VF_iselementC may be used. In order to test for each element of a
vector, if it has an identical entry in a table, VF_iselementV should be
used.



4.6.3 Direct Bit-Manipulation
-----------------------------

For the integer data types, a number of bit-wise operations is available:
VI_shl and VI_shr shift the bits to the left or to the right, resp., which
is used for the fast multiplication and division by integer powers of 2.
The principal use of VI_and is the fast modulo division of positive or
unsigned numbers, while VI_or,  VI_xor, and VI_not  will find use only in
special applications.



4.6.4 Basic Arithmetics, Accumulations
--------------------------------------

In the following list, only the VF_ function is explicitly named, but the
VD_ and VE_ functions exist as well; if it makes sense, the same is true for
the complex and for the integer-type versions:

VF_neg        Yi = - Xi;
VF_abs        Yi =  Xi ;
VCF_conj      Yi.Re = Xi.Re; Yi.Im = -(Xi.Re).
VF_inv        Yi = 1.0 / Xi;
VF_equC       Xi = c;                VF_equV     Yi = Xi;
VF_addC       Yi = Xi + c;           VF_addV     Zi = Xi + Yi;
VF_subC       Yi = Xi - c;           VF_subV     Zi = Xi - Yi;
VF_subrC      Yi = c - Xi;           VF_subrV    Zi = Yi - Xi;
VF_mulC       Yi = Xi  c;           VF_mulV     Zi = Xi  Yi;
VF_divC       Yi = Xi / c;           VF_divV     Zi = Xi / Yi;
VF_divrC      Yi = c / Xi;           VF_divrV    Zi = Yi / Xi;
VF_modC       Yi = Xi mod c;         VF_modV     Zi = Xi mod Yi.

Besides these basic operations, several frequently-used combinations of
addition and division have been included, not to forget the Pythagoras
formula:

VF_hypC    Yi = Xi / (Xi + c);        VF_hypV    Zi =  Xi / (Xi + Yi);
VF_redC    Yi = (Xi * c) / (Xi + c);  VF_redV    Zi =  (Xi * Yi) / (Xi + Yi);
VF_visC    Yi = (Xi - c) / (Xi + c);  VF_visV    Zi =  (Xi - Yi) / (Xi + Yi);
VF_hypotC  Yi = sqrt( Xi + c );     VF_hypotV	 Zi =   sqrt( Xi + Yi).

All functions in the right column of the above two sections also exist in an
expanded form (with the prefix VFx_...) in which the function is not
evaluated for Xi itself, but for the expression (a * Xi + b),  e.g.,
VFx_addV:   Zi  =  (a * Xi + b) + Yi.

The simple algebraic functions exist also in yet another special form,
with the result being scaled by some arbitraty factor. This scaled
form gets the prefix VFs_.
VFs_addV     Zi = C * (Xi + Yi);
VFs_subV     Zi = C * (Xi - Yi);
VFs_mulV     Zi = C * (Xi * Yi);
VFs_divV     Zi = C * (Xi / Yi);

VF_maxC     sets Yi equal to Xi or C, whichever is greater;
VF_minC     chooses the smaller of Xi and C;
VF_maxV     (and  VF_minV) set Zi equal to Xi or Yi, whichever is greater
            (or smaller, resp.).
VF_limit    limits the range of values, while
VF_flush0   sets all values to zero which are below a preset threshold.
VF_intfrac  splits the numbers into their integer and fractional parts;
VF_mantexp  splits the numbers into their mantissa and exponent parts.

In its geometrical interpretation, a vector is a pointer, with its elements
representing the coordinates of a point in n-dimensional space. There are a
few functions for geometrical vector arithmetics, namely
VF_scalprod,  which calculates the scalar product of two vectors,
VF_xprod,     which calculates the cross-product (or vector product) of two
              vectos, and
VF_Euclid,    calculating the Euclidean norm of a vector.

While, in general, all OptiVec functions are for input and output vectors
of the same type, there exists one family of functions for the accumulation
of data in either the same type or in higher-precision data types.
These functions correspond to the operation Y += X (or inc(Y, X) ).
The same-type variant is called VF_accV;  examples for the mixed-type
forms are VD_accVF, VF_accVI, and VQI_accVLI.


4.6.5 Powers
------------

VF_square,  VF_cubic, and VF_quartic, along with their expanded versions
VFx_square,  VFx_cubic,  and VFx_quartic, are used to calculate the second,
third and fourth power of the elements of the input vector. Arbitrary
integer powers are available by VF_ipow; fractional powers are calculated by
VF_pow. Polynomials are evaluated by VF_poly.

In situations where you can be absolutely sure that all input elements
yield valid results, you may employ the "unprotected" versions of the
integer power functions: VFu_square, VFu_cubic, VFu_quartic, VFu_ipow,
with their expanded counterparts denoted by the prefix VFux_ . Due to the
much more efficient vectorization permitted by the absence of error checks,
the unprotected functions are up to 1.8 times as fast as the protected
versions. (This is true from the Pentium CPU on; on older computers,
almost nothing is gained.) Be, however, aware of the price you have to
pay for this increase in speed: In case of an overflow error, the program
will crash without any warning.

All these functions raise arbitrary numbers to specified powers, whereas the
following group of functions is used to raise specified numbers to arbitrary
powers: VF_pow10, VF_ipow10,  VF_pow2,  and VF_ipow2  raise 10 or 2, resp.,
to the (fractional or integer) powers specified in the input vector.
The exponential function, VF_exp, raises Euler's constant e to the powers
specified in the input vector. Finally, VF_expArbBase calculates the
exponential function of an arbitrary base.
The square-root, which corresponds to a power of 0.5,  is available with
VF_sqrt.

The corresponding functions for complex numbers are VCF_square,  VCF_cubic,
VCF_quartic,  VCF_ipow,  VCF_pow,  VCF_exp,  VCF_expArbBase, and VCF_sqrt.



4.6.6 Exponentials and Hyperbolic Functions
-------------------------------------------

A variety of functions are derived from the exponential function VF_exp (which
itself has already been mentioned in the last section).
VF_expc   calculates the complementary exponential function Yi = 1 - exp[Xi].
VF_expmx2 calculates the exponential function of the negative square of the
          argument,  Yi = exp[ - Xi ]. This is a bell-shaped function similar
          to the Gaussian distribution function which itself is available as
VF_Gauss.

Related to VF_Gauss and to VF_exp, the error function and the complementary
error function are calculated by VF_erf and VF_erfc, respectively.

The vectorized hyperbolic sine, cosine, tangent, cotangent, secant, and
cosecant functions are available as VF_sinh, VF_cosh, VF_tanh, VF_coth,
VF_sech, and VF_cosech. Because of its importance in physics, the squared
hyperbolic secant is also available as VF_sech2.

For complex numbers,  VCF_sinh,  VCF_cosh,  and VCF_tanh are available.



4.6.7 Logarithms
----------------

           The decadic logarithm (i.e., the logarithm to the basis 10) is
           available as
VF_log10,  the natural logarithm (i.e., to the basis e) is obtained by
VF_log,    and the binary logarithm (i.e., to the basis 2) is implemented as
VF_log2.   Similarly, for complex numbers,
VCF_log,  VCF_log10, and VCF_log2  (as always with their VCD_ and VCE_
           counterparts) are included.

As a special form of the decadic logarithm, the Optical Density,
OD = log10( X0/X ), is calculated by  VF_OD  (for floating-point input
vectors) and VUS_ODtoF, VUB_ODtoF etc. (for unsigned-integer input vectors).
VF_ODwDark, VUS_ODtoDwDark, etc. allow to calculate the OD with a correction
for dark currents.



4.6.8 Trigonometric Functions
-----------------------------

The vectorized sine, cosine, tangent, cotangent, secant, and cosecant
functions are available as
VF_sin,       VF_cos,
VF_sincos     (sine and cosine at once!),
VF_tan,       VF_cot,
VF_sec,       and VF_cosec.

The squares of the trigonometric functions are available by
VF_sin2,      VF_cos2,
VF_sincos2    (again both the sin and the cos at once),
VF_tan2,      VF_cot2,
VF_sec2,      and VF_cosec2.

In cases where one knows beforehand that all input elements are witin a
range -Pi/2 <= x <= +Pi/2,  one can spare quite considerable execution
time in the calculation of the sine and cosine functions by employing
the "reduced-range" functions
VFr_sin,   VFr_cos,   VFr_sincos,
VFr_sin2,  VFr_cos2,  VFr_sincos2,
along with their expanded counterparts, denoted by the prefix VFrx_ .
Please note that especially the implementation chosen for the 32-bit
model FLAT will crash without warning in the case of any input number
outside the range specified above.
As all other trigonometric functions need error checking and handling,
even for arguments within this range, no reduced-range versions of the
trigonometric functions, aside from the sine and the cosine, have been
included.

A very efficient way to calculate the trigonometric functions for arguments
representable as rational multiples of Pi is supplied by the trigonometric
functions with the suffix "rpi" (meaning "rational multiple of p"):
VF_sinrpi, VF_cosrpi, VF_sincosrpi, VF_tanrpi, VF_cotrpi, VF_secrpi, and
VF_cosecrpi.
More specialized versions use tables to obtain frequently-used values;
these versions are denoted by the suffixes "rpi2" (multiples of Pi divided
by an integer power of 2) and "rpi3" (multiples of Pi over an integer
multiple of 3). Examples are VF_sinrpi2 and VF_tanrpi3.

The sinc function (quotient of the sine of an argument and the argument
itself) is available as the VF_sinc.

The Kepler function (angular position of a planet with time, given its
round-trip time and eccentricity, according to Kepler's Second Law) is
implemented as VF_Kepler.

Vectorized inverse trigonometric functions are available as VF_asin,
VF_acos, VF_atan, and VF_atan2.

Complex trigonometric and inverse trigonometric functions are implemented as
VCF_sin,  VCF_cos,  VCF_tan,  VCF_asin,  VCF_acos,  and VCF_atan.



4.7 Analysis
------------

Global maxima and minima of real functions are detected by VF_max and
VF_min, resp. The same extrema, along with the index of their first
occurrence, are detected by VF_maxind and VF_minind, resp. To find the
maxima and minima in terms of absolute values, the functions VF_absmax and
VF_absmin are included along with the versions additionally yielding the
index, VF_absmaxind and VF_absminind. The "running" maximum and minimum
(where each element is set to the largest/smallest value occurring up to its
own index) are calculated by VF_runmax and VF_runmin, resp.

For complex numbers, the maximum real and imaginary parts may be found
separately by VCF_maxReIm, with the analogous function for the minima being
VCF_minReIm. For the seperately-found maxima and minima of the real and
imaginary parts in absolute terms, use VCF_absmaxReIm and VCF_absminReIm.
Note that, for these four functions, the real and imaginary parts of
the result generally stem from different elements of the vector.

The largest absolute value (magnitude) occurring in a set of complex data is
found by VCF_absmax, the smallest one by VCF_absmin. To find the index of
the element with the largest/smallest magnitude along with that magnitude,
use VCF_absmaxind and VCF_absminind, resp.

The sum of all elements of a real or complex vector is available by VF_sum
and its higher-accuracy or complex analogues, the product by VF_prod and the
sum-of-squares by VF_ssq.  VF_rms determines the r.m.s. of all elements of
a vector.
Similarly to the "running" maximum, the running sum and product are available
by VF_runsum and VF_runprod, resp.

The derivative of a Y-array with respect to an X-array is calculated by
VF_derivV. If the intervals between the X-values are constant, the values
themselves are not needed for taking the derivative, but only the spacing is
required; VF_derivC should be employed in this case.
The integral of a Y-array over an X-array is calculated by the two functions
VF_integralV and VF_runintegralV, of which the first one determines only the
area under the curve defined by the input array, whereas the second one
calculates the point-by-point integral array. As for the derivative, also for
the integral the X-values themselves are not needed if they are equally-
spaced; in this case, VF_integralC and VF_runintegralC should be used.

VF_ismomoton   tests if an array is monotonously rising or falling.
VF_smooth      (which removes high-frequency noise),
VF_iselementC  (which tests, if a given value occurs within a vector), and
VF_searchC     (which searches an ordered table for the entry whose value
               comes closest to a preset value C) have to be mentioned as
               functions sometimes needed in connection with analysis.



4.8 Signal Processing:
    Fourier Transforms and Related Topics
-----------------------------------------

The forward and the backward Fast Fourier Transform (FFT) are calculated by
VF_FFT or, for complex vectors, by VCF_FFT.

Based on FFT, convolution and deconvolution are available by
VF_convolve and VF_deconvolve.
Spectral filtering is achieved by VF_filter,
spectral analysis by VF_spectrum.
The autocorrelation function of a data array is obtained by VF_autocorr,
and the cross-correlation function of two arrays by VF_xcorr.

The FFT algorithm chosen for this PC implementation is a radix-2
Cooley-Tukey routine. Only for this radix-2 algorithm, the restricted
number of eight coprocessor registers still allows to hold all inter-
mediate results of the inner transform loop in coprocessor registers.
Although featuring savings in the number of multiplications, radix-4 and
radix-8 routines are rendered less efficient than the routine chosen by
the need of storing intermediate results in memory.

The already-mentioned table of sine values (see chapter 4.6.8. and the
function VF_sinrpi2) is used here as a look-up table for the Fourier
coefficients needed. This table needs up to 10 kBytes. In case of extreme
memory stress, you may, however, use another variant of FFT. This other
variant employs trigonometric recursions to obtain the sine and cosine
values with still satisfactory speed, though this procedure is not as fast
as simply reading them from a table. You may specifiy this variant by
adding the letter "s" (for the "smaller" amount of memory needed) in the
function prefix. Examples are VFs_FFT, VDs_convolve, VEs_spectrum.
If you wish to use this variant, use the prefix VFs_ for all(!) routines
employing FFT. Otherwise, you will not only load the look-up table, but
also a second FFT routine into your already overcrowded memory.

Although it does not use Fourier transform methods, VF_smooth should be
remembered here as a crude form of frequency filtering which removes high-
frequency noise.



4.9 Statistical Functions and Building Blocks
---------------------------------------------

The mean (or average) of all the elements of a vector is obtained by
VF_mean; if different weights are to be attributed to the individual
elements, VF_meanwW ("mean with weights") may be used. The variance of a
distribution with respect to a preset constant value is calculated by
VF_varianceC (with weights by VF_varianceCwW), the variance with respect to
another array by VF_varianceV and VF_varianceVwW. To obtain the mean and the
variance of a distribution simultaneously, VF_meanvar and VF_meanvarwW are
used.
VF_meanabs calculates the mean of the absolute values.
If outlier points are to be excluded from the calculation of the mean,
VF_selected_mean allows to average only those vector elements which fall
into a specified range.

The median of a distribution is found by VF_median.

The linear correlation coefficient of two distributions is available by
VF_corrcoeff.

VF_sumdevC   sums up the deviations from a preset constant, sum( Xi - C ).
VF_sumdevV   sums up the deviations from another vector, sum( Xi - Yi ).
VF_avdevC    gives the "average deviation from a preset constant",
                     1/N * sum( Xi - C ), and
VF_avdevV    gives the "average deviation from another vector",
                     1 / N * sum( Xi - Yi ).
VF_ssqdevC   yields the "sum of the squares of the deviations from a preset
             constant",  sum( (Xi - C) ),
VF_ssqdevV   the "sum of the squares of the deviations from another vector",
             sum( (Xi - Yi) ).
VF_chi2      calculates the chi-square merit function, while
VF_chiabs    calculates a more "robust" merit function, summing up absolute
             instead of squared deviations.


A linear regression is performed on X-Y data by VF_linregress or, if the
individual data points are to be weighted, by VF_linregresswW.

Fitting of data sets to arbitrary functions is available in MatrixLib,
which contains the functions VF_polyfit, VF_linfit, VF_nonlinfit,
VF_multiLinfit, and VF_multiNonlinfit (see MATRIX.TXT).

VF_distribution bins data into a discrete one-dimensional distribution
function.

In connection with statistics, the functions VF_sum, VF_prod, VF_ssq,
VF_rms, and VF_iselementC should be remembered.


4.10 Input and Output
---------------------

There are several ways of printing the elements of a vector:
VF_cprint   prints them to the screen (or "console" - hence the "c" in the
            name) into the current text window, automatically detecting its
            height and width. After printing one page, the user is prompted
            to continue.
VF_print    is similar to VF_cprint in that the output is directed to the
            screen, but there is no automatic detection of the screen data;
            a default linewidth of 80 characters is assumed, and no division
            into pages is made.
            Both VF_print and VF_cprint should not be used within TurboVision.
            VF_cprint is not available under Windows. VF_print is available
            for DOS and EasyWin applications, but not for genuine (i.e., OWL
            and Delphi) Windows programs.
VF_fprint   prints a vector to a stream. This may be a file, a printer, or
            again the screen. Nothing will prevent you from mis-using this
            function for printing to the screen in TurboVision or Windows,
            but you should not!
            VF_fprint is available in any environment (DOS, EasyWin and OWL).

VF_write   writes data in ASCII format in a stream
VF_read    reads a vector from an ASCII file.
VF_nwrite  writes n vectors of the same data type as the columns of a table
           into a stream.
VF_nread   reads the columns of a table into n vectors of the same type.

VF_setWriteFormat, VF_setWriteSeparate and VF_setNWriteSeparate allow
to modify the standard settings of VF_write and VF_nwrite.
For the whole-number variants of the ..read functions, a radix different
from the standard of 10 may be defined using V_setRadix.
V_setRadix does, however, not act on VQI_read.

VF_store and VF_recall are employed to store and retrieve data in binary
format (which is much faster and occupies fewer bytes of disk space than
ASCII format).



4.11 Graphics
-------------

VectorLib includes a range of data-plotting routines.
Before any of them may be used, VectorLib graphics has to be initialized.
For DOS programs, this is done by V_initGraph. By calling V_initGraph, the
BGI functions (on which VectorLib's graphics functions rely) are
automatically initialized, too. Do not call initgraph after V_initGraph.
If you have already called initgraph, do not use V_initGraph, but V_initPlot
instead of it. At the end of the graphics session, the Borland Pascal function
closegraph has to be used to leave the graphics mode and to release graphics
buffer memory.

For Windows programs (Delphi or OWL), VectorLib graphics has to be
initialized by V_initPlot. No shut-down is needed at the end, since the
Windows graphics functions always remain accessible.

V_initPlot automatically reserves a part of the screen for plotting
operations. This part comprises about 2/3 of the screen on the right side.
Above, one line is left for a heading. Below, a few lines are left empty.
To change this default plotting region, call V_setPlotRegion after V_initPlot.

Only under Windows, all VectorLib plotting functions may directly be
used for printing on a printer. If this is desired, you have to call
V_initPrint instead of V_initPlot. By default, one whole page is reserved
for plotting. In order to change this, call V_setPlotRegion after V_initPrint.

VectorLib distinguishes between two sorts of plotting functions,
AutoPlot  and  DataPlot.

All AutoPlot functions (e.g., VF_xyAutoPlot) execute the following steps:
*   define a viewport within the plotting region (which is either the
    default region or the one defined by calling V_setPlotRegion)
*   clear the viewport
*   generate a Cartesian coordinate system with suitably scaled and labeled
    axes
*   plot the data according to the parameters passed to the function

All DataPlot functions (e.g. VE_yDataPlot) execute only the last of these
steps. They assume that a coordinate system already exists from a previous
call to one of the AutoPlot functions. The new plot is added to the existing
one. All settings of this coordinate system have to be valid. The viewport
must still be the active one and the scaling of the axes has to fit also for
the new data plot.

To add text and lables, a new viewport must be defined.
Use SetViewPort (Pascal for DOS), SetViewportOrg (Pascal for Windows), or
SetViewportOrgEx (Delphi).
To switch back into text mode in DOS, use RestoreCRTmode.
After that, calling V_initPlot brings you back into graphics mode.

VF_xyAutoPlot   displays an automatically-scaled plot of an X-Y vector pair.
VF_yAutoPlot    plots a single Y-vector, using the index as X-axis.
VF_xy2AutoPlot  and
VF_y2AutoPlot   plot two X-Y pairs or two Y-vectors at once, doing the
                necessary scaling so that both fit into the same coordinate
                system.
                To plot additional arrays into an already existing coordinate
                system,
VF_xyDataPlot   and
VF_yDataPlot    should be used, as has already been mentioned.

Complex arrays are plotted into the complex plane (the imaginary parts
versus the real parts), using
VCF_autoPlot,  VCF_2AutoPlot,  and  VCF_dataPlot.

The different plot styles, regarding symbols, lines, and colors, are
described in connection with VF_xyAutoPlot in the Function Reference
(file FUNCREF.TXT, chapter 8).

It is possible to draw more than one coordinate systems into a given
window on the screen. The position of each coordinate system must be
specified by the above-mentioned function V_setPlotRegion. "Hopping"
between the different coordinate systems and adding new DataPlots
after defining new viewports (e.g., for text output) is made possible
by the following functions:
V_continuePlot    go back to the viewport of the last plot and restore
                  its scalings
V_getCoordSystem  get a copy of the scalings and position of the current
                  coordinate system
V_setCoordSystem  restore the scalings and position of a coordinate system;
                  these must have been stored previously, using
                  V_getCoordSystem

DOS only:
When using multiple coordinate systems on the same screen, the default font
used for axis labeling might be too large, so that neighbouring labels
overlap each other. In these cases, use the BGI function settextstyle to
switch to another font befor calling a VectorLib AutoPlot function.



****************************************************************************
*                                                                          *
*******                     5. Error Handling                        *******
*                                                                          *
****************************************************************************

5.1 General Remarks
-------------------

There are generally two types of error handling: by the hardware, or by the
software. In order to prevent uncontrolled program crash, it is highly
desirable that conditions, leading to hardware errors, be recognized before
the errors actually occur. All high-level computer languages support this
software error-handling to various degrees of perfection. Within the
tightly-defined functions and routines of this VectorLib package, often an
even more efficient error handling by the program itself is possible than
provided by the compilers for user-written code.

However, it should be noted that for the Extended versions, overflow errors
cannot always be prevented, because there is no "safety margin" left as in
the Single and Double versions, where internally all calculations are
performed in Extended precision. Especially the VEx_ and VCEx_versions may
fail if constant parameters are very large, or if the X vector elements
themselves are already near the overflow limit (which may be the case after
errors in preceding functions). The safety limit for constant parameters is
at about 1.E32 for Float (= Single), 1.E150 for Double, and 1.E2000 for
Extended parameters.
In the "expanded" versions of all functions with Extended accuracy (those
with the prefixes VEx_  and VCEx_ as, for example, VEx_exp), there is
generally no overflow protection for the calculation of A*Xi + B, but only
for the core of the function itself and for the final multiplication by C.

In Borland Pascal, run-time errors cause the program to be aborted with
a message stating an error number and the address where the error occured.
The exception handling of Delphi is somewhat more flexible, and VectorLib
allows for a still improved handling of those errors occurring within
VectorLib routines. A message is printed, stating the type of error and the
name of the "complaining" function or procedure. Through the procedure
V_setFPErrorHandling, the user can define the action(s) to be taken for the
various types of floating-point errors. Similarly, the procedure
V_setIntErrorHandling decides how to treat errors within the error-catching
variants of the VectorLib integer functions.

A series of identical errors occurring within one and the same VectorLib
function leads to one error message only. Subsequent identical messages are
suppressed.

There is a fundamental difference between floating-point and integer
numbers with respect to OVERFLOW and DOMAIN errors: for floating-point
numbers, these are always serious errors, whereas for integer numbers,
by virtue of the implicit modulo-2**n arithmetics, this is not necessarily
the case. In the following two paragraphs, details are given on the error
handling of integer and floating-point numbers, respectively.



5.2 Integer Errors
------------------

The only genuine integer errors are ZERODIVIDE errors (if a division by 0 is
attempted). These errors are always treated by the standard error handler of
Borland Pascal/Delphi, i.e., the program is aborted. (In Delphi, you may
also install your own exception handler.)
Other integer errors are neglected due to the implicit definition of integer
operations to be done modulo the respective power of 2 (see chapter 4.4).
For those situations in which implicit modulo 2**n arithmetics is not
appropriate, VectorLib offers the possibility to trap these errors and print
an error message and/or abort the program.
To choose this variant, one has to replace the prefixes VSI_, VI_, or VLI_
with VSIo_, VIo_, or VLIo_, respectively. For the data-type interconversion
functions, the prefix V_ is changed into Vo_. Then, the already-mentioned
procedure V_setIntErrorHandling must be called. It takes an argument of
the type V_ihand (defined in the unit VecLib) with one of three possible
values:
   ierrIgnore,  ierrNote,  or ierrAbort.

If you wish to get a message in the case of INTEGER OVERFLOW (e.g., in
VIo_ramp, VIo_mulV, etc.) or INTEGER DOMAIN errors (e.g., in Vo_ItoU for
negative X-values), you have to call
   V_setIntErrorHandling( ierrNote );

If, in addition to printing a message, the program shall be aborted in case of
one of these errors, call
   V_setIntErrorHandling( ierrAbort );

In principle, you may use a call to
   V_setIntErrorHandling( ierrIgnore );
to switch the error handling off. However, it is always better simply to
use the "normal" VI_ version rather than the VIo_ version with the error-
handling switched off, as the normal version is always much faster.

Example:
    var    SI1, SI2:  siVector;
    begin
        ....

        SI1 = VSI_vector( 1000 );  SI2 = VSI_vector( 1000 );
        V_setIntErrorHandling( ierrNote );
        VSIo_ramp( I1, 1000, 0, 50 );   (* an overflow will occur here! *)
        V_setIntErrorHandling( ierrAbort );
        VSIo_mulC( I2, I1, 1000, 5 );
            (* here, the first overflow breaks your program. *)
         ....
    end;




5.3 Floating-Point Errors
-------------------------

The types of errors occurring in mathematical functions are described in
detail below. How VectorLib handles each type of error is defined by a call
to  V_setFPErrorHandling.  The possible options are set by the fperrXXX
constants defined in the following table. When calling V_setFPErrorHandling,
combine these constants by the OR operator. Note that this influences only
the way errors are handled within VectorLib functions. It does not affect
the way how the standard Borland Pascal/Delphi functions handle errors.

Ŀ
 Constant              Value    Meaning                                   
Ĵ
 fperrIgnore               0   Ignore all floating-point errors: handle   
                               them silently, do not print a message,     
                               continue program execution                 
 fperrNoteDOMAIN       $0001   Print a message in case of a DOMAIN error  
 fperrNoteSING         $0002   Print a message in case of a SING error    
 fperrNoteOVERFLOW     $0004   Print a message in case of an OVERFLOW     
 fperrNoteTLOSS        $0010   Print a message in case of a TLOSS error   
 fperrAbortDOMAIN      $0101   Abort program in case of a DOMAIN error    
 fperrAbortSING        $0202   Abort program in case of a SING error      
 fperrAbortOVERFLOW    $0404   Abort program in case of an OVERFLOW error 
 fperrAbortTLOSS       $1010   Abort program in case of a TLOSS error     
 fperrDefaultHandling  $0107   Same as (fperrAbortDOMAIN or fperrNoteSING 
                                         or fperrNoteOVERFLOW)            


Example:
       V_setFPErrorHandling(  fperrAbortDOMAIN or
                              fperrAbortSING or
                              fperrNoteOVERFLOW or
                              fperrNoteTLOSS );

In this example, program execution will be aborted (with the appropriate
message) in the case of the most severe errors, DOMAIN and SING. In the case
of OVERFLOW and TLOSS errors, a warning will be displayed, but program
execution will be continued with default results set by the respective
functions where the errors occur. As noted above, the repeated occurrence
of the same type of error within one and the same function will lead to only
one message being generated. Subsequent errors will be treated silently.
In the description of all types of errors, following below, we denote by
"HUGE_VAL" the largest number possible in the respective data type.
Similarly, "TINY_VAL" is the smallest denormal number representable in the
respective data type; this is not the same as MIN_VAL, which is the smallest
full-accuracy number of the respective data type.

In addition to the error handling "by element", the return values of the
mathematical functions show if all elements have been processed error-free
(return value FALSE) or if an error occurred and was handled (return value
TRUE).

DOMAIN errors most often lead to the result NAN ("not-a-number"). Even if
    nothing happens within the function itself that detects a DOMAIN error,
    an uncontrolled program crash may result if subsequent operations are
    performed on the vector element set to NAN.
       Note:  the pseudo-numbers INF and NAN are not allowed as input for any
       functions of the VectorLib library. They are not tested for; their
       presence will normally result in a hardware interrupt being generated.

SING errors are treated like an extreme case of OVERFLOW (see below). In
    most cases, they arise from an implicit division by zero or from taking
    the logarithm of zero. The proposed result is never NAN, but always a
    "number", in most cases HUGE_VAL.

OVERFLOW errors are the most abundant form of floating-point errors. They
    are always handled by proposing +HUGE_VAL or -HUGE_VAL as the result.
    Within many user algorithms, OVERFLOW  errors may occur for intermediate
    results; if subsequent steps perform operations like taking the inverse,
    the final result may be acceptable despite the error. Therefore, we
    recommend to accept the default handling of these errors and not to
    abort the program.

TLOSS  ("total loss of precision") are regarded as errors only if a more
    serious error might occur in the respective function. For example,
    the sine function takes on values between -1 and +1 for all arguments.
    So, in case of an argument too big for the sine function to be evaluated
    with any accuracy, the result may nevertheless be "tacitly" set to 0.0
    and no error message will be generated, even if you chose fperrNoteTLOSS
    or fperrAbortTLOSS.
    On the other hand, the cosecant, i.e. the inverse of the sine, is not
    defined for arguments of integer multiples of Pi. Therefore, a more
    serious error (in this case a SING or an OVERFLOW error) might be hidden
    under the TLOSS for very big arguments. This possibility is taken into
    account by handling the error according to the options set via
    V_setFPErrorHandling.
    Generally, the default result in the case of a TLOSS error is the mean of
    the results for arguments of +0.0 and -0.0.

UNDERFLOW errors are never detected; underflowing results are always
    "tacitly" set to denormal numbers or finally to 0.0 by the floating-point
    processor itself. Indeed, you may very rarely wish to do something else
    in this case.

PLOSS ("partial loss of precision") errors are never detected and precision
    problems simply ignored.



5.4 The Treatment of Denormal Numbers
-------------------------------------

"Denormal" are very small numbers between zero and the smallest full-
accuracy number available in the respective data type.  You may
understand the underlying principle from a simplified example:
1.175494E-38 is the smallest "normal" Single, with 7-digit accuracy.
What about 1/1024 of this value? This can only be represented as
0.001145E-38, which is accurate to only four digits, since the first
three digits are needed to hold zeros. Thus, denormal numbers provide a
smooth transition between the smallest representable normal numbers and
zero.

In general, they may be treated just as ordinary numbers. In some instances,
however, like taking the inverse, overflow errors may occur. In these cases,
the somewhat academic distinction between SING and OVERFLOW errors is dropped
and a SING error signalled (as if it was a division by exactly 0).

On the other hand, for functions like the logarithms, very small input numbers
may give perfectly reasonable results, although the exact number 0.0 is an
illegal argument, leading to a SING error. Here, the possible loss of
precision is neglected and denormals are considered valid arguments.



5.5 Advanced Error Handling: Writing Messages into a File
---------------------------------------------------------

In Borland Pascal/Delphi, the way error messages are printed is normally not
controllable by the programmer. While this is fine in most instances, there
may be situations in which you might, for example, wish the error messages
not to be printed to the screen, but rather into a file, so that you could
check later what has gone wrong.
To this end, VectorLib provides the function V_setErrorEventFile.
This function needs as arguments the desired name of your event file and a
switch named  ScreenAndFile  which decides if the error message is printed
only into the file, or additionally to the screen as well. The default
printing of error messages to the screen alone is restored by
V_closeErrorEventFile.

Note that the described redirection of error messages is valid only for
errors occurring in VectorLib routines. Errors occurring outside VectorLib
lead to the standard action of Borland Pascal/Delphi.

A way to keep track also of those errors which do not lead to messages is
opened by the return values of mathematical VectorLib functions. Any of the
"silent" TLOSS along with the more serious DOMAIN, SING and OVERFLOW errors
will lead to a return value of TRUE. You may wish to check for a clean result
after a group of functions, like in the following example:

   ErrFlag:  IntBool;
   ...
         (*   part Trig1  *)
   ErrFlag := FALSE;   (* reset the flag *)
   ErrFlag := ErrFlag or VF_sin( Y1, X1, sz );
   ErrFlag := ErrFlag or VF_cos( Y2, X1, sz );
   ErrFlag := ErrFlag or VF_atan2( Z1, Y1, Y2, sz );
   if ErrFlag then writeln( 'Errors occurred in part Trig1 ! ');
   ...



****************************************************************************
*                                                                          *
*******                    6. Trouble-Shooting                       *******
*                                                                          *
****************************************************************************


6.1 General Problems
--------------------

In case of problems, please check first if OptiVec is correctly installed
(see chapter 1.4). If this is the case, carefully check the following points
whose violation would inevitably lead to failure.

*   You must not use vectors with a size of 0. All functions tacitly assume
    that the vectors have at least one element and do not waste your computer
    time testing for that.

*   You must not use vectors that are only declared, but have no allocated
    memory (see the description of VF_vector).

*   Constant parameters should not exceed 1.E32 for Single, 1.E150 for
    Double,  or 1.E2000 for Extended. Normally, these ranges should
    suffice for any application...

Although OptiVec has been tested very thoroughly, there is, of course,
always the possibility that a problem might have escaped our attention.
Should you feel you discovered a "bug," please try to specify
the situation causing the problem as exactly as possible and let the author
know!



6.2 Problems with Windows?
--------------------------

Programming for 16-bit Windows is much more involved than programming for
DOS. While DOS gives the programmer almost complete control over both the
main processor and the coprocessor, Windows demands much of this control for
itself. This introduces problems you should be aware of. They are not at all
specific to VectorLib. However, since they seem not to be very widely known,
here is a collection of some of them. These problems are not present in
either DOS or Windows95/98/NT.

*   The background routines controlling intermediate results do not only work
    at the expense of your time, they may also at some point decide to load a
    NULL selector into the segment registers FS and GS. If you happen to use
    these registers (somehow, they were meant by Intel to be used), Windows'
    answer on your next operation will be the familiar "General Protection
    Fault (Error 13)". Therefore, the Windows versions of VectorLib do not
    use FS and GS at all.

*   If a multiplication or division happens to result in a so-called
    "denormal number" (see chapter 5.4), Windows at first accepts this
    result. The next time you use this denormal result, however, Windows
    decides that it had better been zero. Checking for zero by a comparison
    like
          if(x != 0.0)...
    yields the correct answer that x it is not zero, but, after (!) this
    check, Windows makes x exactly zero, if it is loaded onto the number
    stack. This leads to hard-to-find errors. If you inspect VectorLib
    routines with the debugger, you may at some points encounter strange,
    seemingly ineffecient code being used for comparisons. This is a fix for
    the described problem which costs time, but saves you from Windows-
    induced DIVIDE ERROR crashes.

*   Related to the last problem is another feature of Windows: after the
    comparison of two floats or two doubles, one of which is denormal, -NAN
    ("minus not-a-number")  may appear on the number stack. Some time later,
    this leads to a "Floating-point invalid" or a "Stack Overflow" error -
    another means of killing your application. If you encounter -NAN on the
    number stack when debugging your programs (with or without VectorLib
    used), you should find out which comparison(s) caused the problem and add
    the line
           asm  ffree ST(0); end;
    after this or these comparisons.



7. The Units of VectorLib
-------------------------

This chapter describes the units containing the VectorLib routines.

VecLib  contains the basic definitions of the data types along with the
functions common to all data types (prefix V_) except for the graphics
initialization functions. The trigonometric tables (see chapter 4.6.8) and
the few non-vectorized math functions needed internally by VectorLib
(see chapter 9), are contained in xmath.

VIstd,    VBIstd,    VSIstd,    VLIstd,    VQIstd,
VUstd,    VUBstd,    VUSstd,    VULstd,
VFstd,    VDstd,     VEstd,
VCFstd,   VCDstd,  and  VCEstd
contain the functions used for generation and initialization of vectors,
for index-oriented manipulations, data-type interconversions, and I/O
operations. For the floating-point data types, they also contain the
prototypes of routines for statistics, analysis, geometrical vector
arithmetics, and Fourier-Transform related functions.
In VFstd, the real-number functions for the data type Float (= Single,
prefix VF_) are to be found, in VDstd those for the data type Double,
and so on.

The algebraic and mathematical functions are contained in the V..math units:
VImath,    VBImath,    VSImath,    VLImath,    VQImath,
VUmath,    VUBmath,    VUSmath,    VULmath,
VFmath,    VDmath,     VEmath,
VCFmath,   VCDmath, and   VCEmath.

Vgraph  contains the graphics and plotting routines for all data types.



*****************************************************************************


For detailed information on each single function of VectorLib, see the

S e c o n d  P a r t :  File  FUNCREF.TXT

 8.  Alphabetical Reference

 9.  Non-vectorized Functions

10. VectorLib Error Messages


*****************************************************************************

Copyright (C) Martin Sander 1996-1999
