       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 2

                  for Borland Pascal and Delphi

            (Borland (Turbo) Pascal 7.0, Delphi 2, 4, 5)

                      OptiCode - Dr. Martin Sander Software Development
                      Steinachstr. 9A
                      D-69198 Schriesheim
                      Germany
                      e-mail: support@optivec.com  or  sales@optivec.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".                                     !!
!!     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: see MATRIX.TXT.

1. Introduction
    1.1 What is VectorLib and Why are the VectorLib Functions so Fast?
    1.2 Licence Terms for the Shareware Version
    1.3 Registered Version
        1.3.1 OptiVec for Borland Pascal
        1.3.2 OptiVec for Borland Delphi
        1.3.3 Licence Terms for the 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, iVector, etc.
    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 most implementations
of the popular BLAS (Basic Linear Algebra 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 a powerful
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/Delphi 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, included
    in 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.

* Preload of floating-point constants
  Floating-point constants, employed in the evaluation of mathematical
  functions, are loaded onto the floating-point number stack outside of
  the actual loop and stay 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.

* Full FPU stack usage
  Where necessary, all eight coprocessor registers are employed. (For 
  present compilers, it is already an excellent achievement to master 
  the bookkeeping for only four coprocessor registers.)

* Superscalar scheduling
  By careful  "pairing" of commands whose results do not depend upon each
  other, the two integer pipes and the two fadd/fmul units of the
  Pentium/Pentium Pro are used as efficiently as possible.
  In most instances, computers equipped with 386/387 or 486DX CPUs just
  will not care about these optimizations which they cannot profit from.
  In those cases, however, where the performance on these older CPUs suffers
  significantly from the Pentium-optimized scheduling, it is applied only in
  the "4" version of OptiVec (back-compatible to 486DX), but not in the "3"
  version (back-compatible to 386/387).

* Loop-unrolling
  Where optimum pairing of commands cannot be achieved for single elements, 
  vectors are often processed in chunks of two, four, or even more elements.
  This allows to fully exploit the parallel-processing capabilities of the
  Pentium and its successors.  Moreover, the relative amount of time spent
  for loop management is significantly reduced.

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

* Replacement of floating-point by integer commands
  For any operations with floating-point numbers that can also be performed
  using integer commands (like copying, swapping, or comparing to preset
  values), the faster method is consistently employed.

* Strict precision control
  C compilers convert a float into a double (Pascal: even into an extended),
  before passing it 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 in the .LIB files, but it is simply
  inefficient on modern PCs. Consequently, no such implicit conversions are
  present in OptiVec routines. Here, a function of a float is calculated to
  float (i.e. single) precision, wasting no time for the calculation of more
  digits than necessary - which would be discarded anyway.

* All-inline coding
  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 passing the parameters forth and back.

* Cache-line matching of local variables
  The Level-1 cache of the Pentium and its presently available successors
  is organized in "lines" of 32 bytes each. Present compilers align the
  stack on 4-byte boundaries, which means there is a 1-in-4 chance that
  the 8 bytes of a double or the 10 bytes of an extended, stored on the
  stack, will cross a 32-byte boundary. This, in turn, would lead to a
  cache line-break penalty, deteriorating the performance. To avoid it,
  OptiVec functions use special procedures to properly align their local
  variables on 8 or 16-byte boundaries.

* Unprotected and reduced-range functions
  For some mathematical functions, you have the choice between the fully
  protected variant with error handling and another, extra-fast variant
  without. Similarly, there are reduced-range versions of the sine and
  cosine functions for those cases in which the user can guarantee all
  input vector elements to lie in the range  -2 Pi <= x <= +2 Pi.
  In these cases, the execution time may be reduced by up to 40% compared
  to the full-range or fully protected version.

* Multithread support
  With very few exceptions (namely the plotting functions, which have to
  use global variables to store the current window and coordinate system
  settings), all other OptiVec functions may run in parallel in different
  threads. On multi-CPU configurations, this means the performance will
  scale with the number of CPUs. OptiVec functions do not initiate threads
  themselves, though, as the overhead involved in multi-threading would
  significantly affect the performance on single-CPU machines. If you
  have a multi-CPU computer, you have to explicitly launch the threads
  you wish to run in parallel. For example, one thread might take the
  lower half of the vector(s) you wish to process, while a second thread
  takes the upper half - until a point is reached, where both must be
  combined.

The implementation described in this documentation refers to
* Borland (Turbo) Pascal, version 7.0 for DOS, or
* Borland Delphi 2, 4, or 5.

Shareware Version:
Depending on your choice when ordering or downloading OptiVec, you have
got either or the above versions. Any of them requires,
at least, a 386 computer equipped with a 387 coprocessor. This means:
no emulation, no 486SX, but preferrably 486DX, Pentium or higher.
Registered Version:
The registered version of OptiVec for Delphi supports Delphi 2, 4, and 5
and has separately optimized libraries for 486/Pentium and 386+387.
The registered version of OptiVec for Borland Pascal has separate units
for 486/Pentium,  386+387,  and 286, both for readl-mode and protected-
mode DOS.

At present, OptiVec is available for
*  Borland Pascal 7.0
*  Delphi 2, 4, 5
*  Borland C++ (all versions from Turbo C++ 3.0 up to BCBuilder 5)
*  Microsoft Visual C++ 5 and 6

For two-dimensional arrays, MatrixLib is included with OptiVec,
offering optimized matrix operations like matrix arithmetics, algebra,
decompositions, data fitting, etc. See MATRIX.TXT.
TensorLib is planned as a future extension of these concepts for general
multidimensional arrays.


1.2 Licence Terms Shareware version
-----------------------------------

The following licence terms apply to the Shareware version of OptiVec.
If you got this file with the Registered version, please see paragraph 1.3
for your valid licence.

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 a period of up to 90 days
   on one computer. This testing phase ends when you permanently integrate
   functions of this SOFTWARE into any of your applications (programs,
   program parts...).
2. Applications, created with the Shareware version of this SOFTWARE, will
   run only on the same computer on which this SOFTWARE has been installed.
   They cannot and may not be distributed to others.
3. If you want to continue using this SOFTWARE after testing, and/or
   if you wish to distribute programs containing functions of this SOFTWARE,
   you have to purchase the registered version (see chapter 1.3).
4. 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-2000 Martin Sander

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

Address of the author:
              Dr. Martin Sander Software Development
              Steinachstr. 9A
              D-69198 Schriesheim
              Germany
              e-mail: MartinSander@optivec.com
              http://www.optivec.com


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

1.3.1 Registered Version of OptiVec for Borland Pascal
------------------------------------------------------

The registered version of OptiVec for Borland Pascal

-  supports the real-mode DOS and DPMI targets of Borland Pascal

-  has individually optimized units for three degrees of
   processor backward-compatibility:
      486DX/Pentium+
      386+ (coprocessor required)
      286+ (with emulation; i.e., no coprocessor required)

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

   and can be ordered directly from the author (e-mail or a printout
   of ORDER.TXT), or through the secure-server ordering service
   "ShareIt" at

   http://www.shareit.com/programs/103423.htm

-  Multi-user licences are available upon request.


1.3.2 Registered Version of OptiVec for Borland Delphi
------------------------------------------------------

In order to make this product affordable also for those who will not
themselves make money using it, we offer an "educational edition" at a
strongly reduced rate, in addition to the full "commercial edition".
The contents of these two editions is identical. The only difference lies
in the restrictions of use: The "educational edition" may not be used for
commercial / business / government purposes, but is restricted to private
and educational use.
Purchasing the full (registered) version gives you the right to use it on
as many computers at a time as the number of units you bought.
The right to distribute applications employing functions of OptiVec
is included in the commercial-version licence. No run-time licence needed! 
Corporate site and world-wide licences are available upon request.

The registered version of OptiVec for Borland Delphi

-  supports Borland Delphi 2, 4, and 5

-  has individually optimized units for two degrees of
   processor backward-compatibility:
      486DX/Pentium+
      386+387

-  entitles you to two years of free updates
   (by downloading from our web site)

-  can be ordered at the following conditions:

   a) if you can pay in German Marks or Euro
      and order directly from the author, the price is
      DM   99,- /  EUR  50,80 for the educational edition,

      DM  199,- /  EUR 102,00 for  1 unit of the commercial edition
      DM  599,- /  EUR 307,20 for  5 units,
      DM 1099,- /  EUR 563,60 for 10 units
         (incl. 16% VAT, plus DM 10,- / EUR 5,- handling charge).
      Please order by sending an e-mail to  sales@optivec.com
      or use a print-out of the file ORDER.TXT.
      Payment options:
          - pre-paid by DM Eurocheque
          - C.O.D. (Cash-On-Delivery)
          - upon invoice (only within Germany, net 14 days)

      If you have a European VAT ID, or if you order from outside the
      European Union, you are exempt from the German VAT, but you may
      have to pay your local VAT and/or import duties according to
      local laws.

   b) International credit card or USD cheque payment is possible by
      ordering through ours or the following web-sites

      Atlantic Coast's SoftShop:
      http://www.swreg.org/soft_shop/47/

      (this is the SoftShop sales page for all our products)

      $  59 for the educational edition,
      $ 119 for  1 unit of the commercial edition,
      $ 349 for  5 units,
      $ 649 for 10 units
      Add $5 for S&H and applicable VAT.

      ShareIt:
      http://www.shareit.com/programs/103843.htm

      $ 124 for the commercial edition (including S&H),
      $  64 for the educational edition (including S&H).
            Add applicable VAT.

      You may also order by e-mail to register@shareit.com.
      US customers can also call 1-800-903-4152 (only for orders, please).
      US check and cash orders can be sent to ShareIt!'s US office at
           ShareIt! Inc.
           P.O. Box 97841
           Pittsburgh, PA 15227-0241
           USA
      * When ordering by e-mail, phone, or postal mail through ShareIt,  *
      * please note the program number:                                  *
      * OptiVec for Delphi:         No. 103843                           *
      *     dto., educational:      No. 103859                           *


1.3.3 Licence Terms for the Registered Version
----------------------------------------------

The following licence terms apply for the Registered version of either
OptiVec for Borland Pascal or OptiVec for Delphi:

This is a single copy license for OptiVec for Pascal/Delphi ("SOFTWARE")
granted by Dr. Martin Sander Software Development.
The SOFTWARE in this package is licensed to you as the user. It is not sold.
Once you have paid the required license fee, you may use the SOFTWARE for as
long as you like, provided you do not violate the copyright and if you
observe the following rules:
1. You may use the SOFTWARE on any computer for which it is designed as long
   as not more than one person uses it at any time.
2. You may make backup copies of the SOFTWARE for your personal use. You may
   only transfer the SOFTWARE to somebody else if you transfer the original
   and all copies, retaining no copies for yourself. You may not lease or
   rent the SOFTWARE to others.
3. You may not decompile, disassemble, or otherwise reverse engineer the
   SOFTWARE into a machine-readable form. You may, however, inspect the
   functions contained in this SOFTWARE by means of debuggers like those
   included in Delphi.
4. If you payed the reduced licence fee for the "educational version" rather
   than the full rate for the "commercial version", the use of this SOFTWARE
   is restricted to private and educational purposes. In this case, you may
   not use the SOFTWARE for commercial purposes or for government purposes
   other than education.
   Applications using functions of this SOFTWARE may be freely distributed
   (i.e. without any run-time licence) only if created with the
   "commercial edition".
5. 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.
6. Martin Sander's liability is limited by the enclosed Limited Warranty.
   In no case shall Martin Sander's liability exceed the license paid for
   the right to use the SOFTWARE.

Limited Warranty for the Registered version
-------------------------------------------
1. Martin Sander warrants that the magnetic or optic media on which the
   SOFTWARE is recorded are free from defects in materials and workmanship
   under normal use. The SOFTWARE itself will perform substantially in
   accordance with the specifications set forth in the documentation.
2. The above express warranties are made for a period of six months from
   the date the SOFTWARE is delivered to you as the first user.
3. Any magnetic/optic or printed media from this package proving defective
   in materials or workmanship will be replaced on an exchange basis.
4. Great care has been taken to ensure that the SOFTWARE operates in
   accordance with the specifications as described in the documentation.
   However, it is not guaranteed that this SOFTWARE will operate completely
   free of errors or that the documentation is free of errors.
5. Any implied warranties including any warranties of merchantability or of
   fitness for a particular purpose are limited to the terms of the above
   express warranties.
6. Martin Sander shall not in any case be liable for special, incidental,
   consequential, indirect or other damages arising from any breach of these
   warranties or of the license conditions, even if he has been notified of
   the possibility of such damages.

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


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 directory "OPTIVEC" will be created to hold the
    OptiVec documentation.
    Shareware version:
        Pascal: The units will be installed into the directory
                OPTIVEC\UNITS.
        Delphi: The units (.DCU files) will be installed into the directory
                OPTIVEC\LIB.
    Registered version:
        Pascal: The units for 386+387 will be installed into the directory
                OPTIVEC\UNITS.
                The additional units for 486/Pentium and for 286 are contained
                in the files UNITS2.ZIP and UNITS4.ZIP and are not auto-
                matically installed. If you wish to use them, manually create
                subdirectories OPTIVEC\UNITS4 or OPTIVEC\UNITS2 and unzip the
                files mentioned into them.
        Delphi: The installation routine you have to execute is named after
                the target Delphi version: INSTALL2.EXE, INSTALL4.EXE, and
                INSTALL5.EXE.
                The units (.DCU files) for 486 will be installed into
                OPTIVEC\LIB3, those for 486 into OPTIVEC\LIB4.

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

*   Borland Pascal only:
    Choose the target DOS real mode or (for the registered version only)
    DOS protected-mode.
    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.

*   Declare the use of OptiVec units as usual with the "uses" statement.
    The OptiVec units are grouped according to the data type. See chapter 7.
    In Delphi, OptiVec units should always be used together with the WinProcs
    unit, as it borrows some data types from it.



****************************************************************************
*                                                                          *
*******               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 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 Turbo Pascal. The WinProcs unit of Delphi offers
already a more systematic nomenclature. In order to make the derived function
prefixes compatible with the C and Fortran versions of VectorLib, we define
those synonyms present in Delphi (and a few more) also for 16-bit Pascal,
as described in the following table:

Ŀ
   type              Pascal 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_          


Pascal and Delphi2 only:
  The unsigned 32-bit integer type ULong is treated as such only
  inside VectorLib routines. Otherwise, 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. As yet, there is nothing like a "UQuad".

Delphi 4 or higher:
  As Delphi 4+ supports 64-bit integers, QuadInt is not defined as Comp, but
  is used as a synonym for the new data type Int64.

To have a Boolean data type available which is of the same size as Integer,
we define the type IntBool. It is equivalent to WordBool in Pascal, but
LongBool in Delphi.
You will see the IntBool 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,
                biVector, siVector, iVector,  liVector,  qiVector,
                ubVector, 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:
   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".

In your programs, you may mix the vector types with the static arrays of
traditional Pascal style, as well as with the new dynamic arrays of Delphi 4+.
Here is a comparison of the pointer-based vectors of VectorLib with the
array types of Pascal/Delphi:

Ŀ
               VectorLib vectors              static/dynamic arrays      
Ĵ
alignment of   32-byte boundary for           4-byte boundary (may cause 
first element  optimum cache-line             line-break penalty for     
               matching                       double, QuadInt)           
                                                                         
alignment of   packed (i.e., no dummy bytes   arrays must be declared as 
following      between elements, even for     "packed" for Delphi 4+ to  
elements       8, 10, and 16-bit types)       be compatible with         
                                              VectorLib                  
                                                                         
index range    none                           automatic with built-in    
checking                                      size information           
                                                                         
dynamic        function VF_vector,            procedure SetLength        
allocation              VF_vector0            (Delphi 4 only)            
                                                                         
initialization optional by calling            always                     
with 0         VF_vector0                                                
                                                                         
de-allocation  function V_free, V_freeAll     procedure Finalize         
                                              (Delphi 4 only)            
                                                                         
accessing      function VF_element:           index in brackets:         
elements       a := VF_element(X,5);          a := X[5];                 
               Delphi4 only: typecast                                    
               into array also possible:                                 
               a := fArray(X)[5];                                        
                                                                         
setting        function VF_Pelement:          index in brackets:         
elements       VF_Pelement(X,5)^ := a;        X[5] := a;                 
               Delphi 4+ only: typecast                                  
               into array also possible:                                 
               fArray(X)[5] := a;                                        
                                                                         
passing to     directly:                      address-of operator:       
vector         VF_equ1( X, sz );              VF_equ1( @X, sz );         
function                                                                 
                                                                         
passing sub-   function VF_Pelement:          address-of operator:       
vector to      VF_equC( VF_Pelement(X,10),    VF_equC( @X[10], sz, 3.7); 
vector func.            sz, 3.7 );                                       
  

Summarizing the properties of VectorLib vectors and of Pascal/Delphi arrays,
the latter are somewhat more convenient and, due to the index range checking,
safer, whereas the pointer-based VectorLib vectors are processed faster (due
to the better alignment and to the absence of checking routines).
As is also shown in the table, you can use Pascal/Delphi arrays with
VectorLib functions by applying the address-of operator on them.


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

The OptiVec 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 Pascal/Delphi, but they are possible in C/C++. Therefore, the
syntax of those functions returning a complex number is different in C/C++
and Pascal/Delphi.



2.6 Functions of the Integer Data Types:
    The Prefixes VI_, VBI_, VSI_,  VLI_, VQI_,
                 VU_, VUB_, 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,
VBI_ stands for ByteInt,
VSI_ for SmallInt,
VLI_ for LongInt, and
VQI_ for QuadInt;
VU_ denotes operations with UInt,
VUB_ works on unsigned bytes (UByte),
VUS_ 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
---------------------------------------------------

With the Shareware version, depending on your choice when downloading or
ordering, you got OptiVec units for either of the following:
* the target real-mode DOS of Borland Pascal 7.0
* Delphi 2
* Delphi 4
* Delphi 5.

Units for the DOS protected-mode of Borland Pascal are available only
in the registered version.

If you followed the standard installation of the Shareware version, add
C:\OPTIVEC\UNITS      to the unit search path of Borland Pascal or
C:\OPTIVEC\LIB        to the search path of Borland Delphi.

Additional units, backwards-compatible with the 286 processor (Pascal only),
and for the most advanced version of OptiVec, optimized for
Pentium/Pentium Pro and requiring at least a 486DX, are contained in the
registered version only.



****************************************************************************
*                                                                          *
*******           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. 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 all vectors at once, use V_freeAll.

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_equm1  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",
byte-size, short, long and "quadruple" integers is performed by
V_ItoBI,  V_ItoSI,  V_ItoLI,  V_ItoQI,
V_BItoI,  V_BItoSI, V_BItoLI, V_BItoQI,
V_SItoI,  V_SItoBI, V_SItoLI, V_SItoQI,
V_LItoI,  V_LItoBI, V_LItoSI, V_LItoQI,
V_QItoI,  V_QItoBI, V_QItoSI,  and V_QItoLI.

Similarly, the available types of unsigned numbers are interconverted by
V_UtoUB,  V_UtoUS,  V_UtoUL, 
V_UBtoU,  V_UBtoUS, V_UBtoUL,
V_UStoU,  V_UStoUB, V_UStoUL,
V_ULtoU,  V_ULtoUB, and V_ULtoUS.

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_BItoUB, V_UBtoBI,
V_SItoUS, V_UStoSI,
V_LItoUL,  and  V_ULtoLI.
That means that functions like V_UStoLI do  n o t  exist!

The conversion of integers into floating-point types is accomplished by
V_ItoF,      V_ItoD,      V_ItoE,
V_BItoF,     V_BItoD,     V_BItoE,
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_UBtoF,     V_UBtoD,     V_UBtoE,
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
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 and VF_linfit (see MATRIX.TXT).
Non-linear and multiple-data fitting functions are not yet available for
the Pascal/Delphi version of MatrixLib.

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.
            Pascal only:
               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) Windows programs.
            Delphi only:
               Both VF_print and VF_cprint can be used only in console
               applications.
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

When using multiple coordinate systems on the same screen, or when
the plot area is less than the full screen, the default font
used for axis labeling will be too large, so that neighbouring labels
overlap each other. In these cases, you have to switch to another font
befor calling a VectorLib plotting 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, but it is often
difficult to recover from math errors.
This is why VectorLib has its own way for handling math errors occurring
within VectorLib routines. In contrast to Delphi exceptions (which are
thrown as a reaction on hardware-detected failure), the VectorLib functions
catch math errors, before they actually occur. 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.

For Delphi, you should always include the unit WinProcs together with
any OptiVec (VectorLib) unit, as OptiVec borrows some data types from
WinProcs.

The unit 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-2000
