OptiVec

Version 3


for C/C++ and for Pascal/Delphi

OptiCode
Dr. Martin Sander Software Development
Steinachstr. 9A
D-69198 Schriesheim
Germany
http://www.optivec.com
e-mail: support@optivec.com or
sales@optivec.com

Part I. A: Handbook

This HANDBOOK describes the basic principles of the OptiVec libraries and gives an overview over VectorLib, the first part of OptiVec. The new object-oriented interface, VecObj, is described in chapter 3. The other parts have their own descriptions in separate files, see MATRIX.HTM and CMATH.HTM.
Chapter 1.2 of this Handbook contains the licence terms for the Shareware version, Chapter 1.3 for the Registered version.
 
OptiCode™ and OptiVec™ are trademarks of Dr. Martin Sander Software Dev. Other brand and product names mentioned in this handbook for identification purposes are trademarks or registered trademarks of their respective holders.
 
German-speaking users:
Um die Kosten für das Herunterladen der Shareware-Version über das Internet für alle so gering wie möglich zu halten, enthält diese nur die englische Dokumentation. Sie finden die deutsche Beschreibung separat unter http://www.optivec.com/download/OVDOCD.ZIP.

Contents

1. Introduction
1.1 Why Vectorized Programming Pays Off on the PC
1.2 License Terms for the Shareware Version
1.3 Registered Versions
 1.3.1 Registered Versions: Ordering
 1.3.2 License Terms for the Registered Versions
1.4 Getting Started
2. The Elements of OptiVec Routines
2.1 Synonyms for Some Data Types
2.2 Complex Numbers: The Data Types fComplex, dComplex, eComplex, fPolar, dPolar, and ePolar
2.3 Vectors and Arrays: The Data Types fVector, dVector, eVector, cfVector, pfVector, iVector, uVector, etc.
2.4 Real-number Functions: The Prefixes VF_,  VD_, and VE_
2.5 Complex-Number Functions: The Prefixes VCF_,  VCD_,  VCE_,  VPF_,  VPD_, and VPE_
2.6 Functions of the Integer Data Types: The Prefixes VI_,  VBI_,  VSI_,  VU_, etc.
2.7 Common Functions of Several Data Types: The Prefix V_
3. C++ only: VecObj, the Object-Oriented Interface for VectorLib
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 Geometrical Vector Arithmetics
4.6.6 Powers
4.6.7 Exponentials and Hyperbolic Functions
4.6.8 Logarithms
4.6.9 Trigonometric Functions
4.7 Analysis
4.8 Signal Processing: Fourier Transforms and Related Topics
4.9 Statistical Functions and Building Blocks
4.10 Data Fitting
4.11 Input and Output
4.12 Graphics
5. Error Handling
5.1 General Remarks
5.2 Integer Errors
5.3 Floating-Point Errors
5.3.1 C/C++ specific
5.3.2 Pascal/Delphi specific
5.3.3 Error Types (Both C/C++ and Pascal/Delphi)
5.3.4 Differences between Borland C++ 4.0 and earlier BC++ versions
5.4 The Treatment of Denormal Numbers
5.5 Advanced Error Handling: Writing Messages into a File
5.6 OptiVec Error Messages
6. Trouble-Shooting
6.1 General Problems
6.2 Problems with Windows 3.x?
6.3 Problems with 32-bit Windows?
6.4 Problems with Borland's 16-bit Linker?
7. The Include-Files and Units of OptiVec

P a r t I. B : File FUNCREF.HTM contains:
 
8. Alphabetical Reference of Vector Functions
9. Scalar (non-vectorized) Functions


1. Introduction

OptiVec offers a powerful set of routines for numerically demanding applications, making the philosophy of vectorized programming available for C/C++ and Pascal/Delphi languages. It 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.

In comparison to the old vector language APL, OptiVec has the advantage of being incorporated into the modern and versatile languages C/C++ and Pascal/Delphi. 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 convenient 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. In comparison to these approaches, OptiVec 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!

There is a certain overlap in the range of functions offered by OptiVec and by BLAS, LINPACK, and other libraries and source-code collections. However, the latter must be compiled, and, consequently, their performance is determined mainly by the quality of the compiler chosen. To the best of our knowledge, OptiVec, was the first product on the market offering a comprehensive vectorized-functions library realized in a true Assembler implementation.

The wide range of routines and functions covered by OptiVec, the high numerical efficiency and increased ease of programming make this package a powerful programming tool for scientific and data analysis applications, competing with (and often beating) many high-priced integrated systems, but imbedded into your favourite programming language.

This documentation describes the OptiVec implementations for


Please note that only the "outside appearance" and thus the documentation is the same for these different compilers. The libraries themselves are compiler-specific; each library can be used only with one compiler and, in the case of C/C++, with one memory model or one target:
  1. Shareware version for Borland C++:
    Depending on your choice when ordering or downloading, you have got either of the following three library versions: All of them require, at least, a 386 computer equipped with a 387 coprocessor. This means: no emulation, no 486SX, but preferably 486DX, Pentium or higher. As of now, the P6 and P7 libraries (for Pentium III, Pentium IV or higher) are included only with the registered version.

  2. Registered version for Borland C++:
    The full (registered) version for Borland C++ contains libraries for all memory models of DOS, 16-bit Windows and 32-bit Windows. These libraries, in turn, are shipped in five versions: one for P7 (Pentium IV or higher) computer, the second for P6 (Pentium III or higher), the third for 486DX and Pentium, the fourth for 386 with 387, and – still available – the fifth for 286 with or without coprocessor, i.e. with emulation. The P6 and P7 versions should be used only with BC++ 5.x, BCB 5 or higher.

  3. Microsoft Visual C++:
    The Shareware version has 486DX/Pentium libraries for "single-thread debug" and "multi-thread debug". The full (registered) version for Microsoft Visual C++ contains additional libraries for "multi-thread DLL debug" and the three corresponding release libraries, in three versions for P7 (Pentium IV or higher), for P6 (Pentium III or higher), and for 486/Pentium+. There is no actual debug information enclosed in the OptiVec "debug" libraries, but they have to be used with the debug libraries of Visual C++.

  4. Borland Delphi: The Shareware version requires, at least, a 386 computer equipped with a 387 coprocessor. This means: no emulation, no 486SX, but preferrably 486DX, Pentium or higher. The Registered version has separately optimized libraries for P7 (Pentium IV or higher), for P6 (Pentium III or higher), for 486/Pentium, and for 386+387.

  5. Borland Pascal 7.0: The Shareware version is for the real-mode DOS target and requires, at least, a 386 computer equipped with a 387 coprocessor. The registered version of OptiVec for Borland Pascal has separate units for 486/Pentium and 386+387, both for real-mode and protected-mode DOS. 286 fossils are no longer supported.

Back to Table of Contents

1.1 Why Vectorized Programming Pays Off on the PC

To process one-dimensional data arrays or "vectors", a programmer would normally write a loop over all vector elements. Similarly, two- or higher-dimensional arrays ("matrices" or "tensors") are usually processed through nested loops over the indices in all dimensions. The alternative to this classic style of programming are vector and matrix functions.
Vector functions act on whole arrays/vectors instead of single scalar arguments. They are the most consequent form of "vectorization", i.e., organisation of program code (by clever compilers or by the programmer himself) in such a way as to optimize vector treatment.

Vectorization has always been the magic formula for supercomputers with their multi-processor parallel architectures. On these architectures, one tries to spread the computational effort equally over the available processors, thus maximizing execution speed. The so-called "divide and conquer" algorithms break down more complicated numerical tasks into small loops over array elements. Sophisticated compilers then find out the most efficient way how to distribute the array elements among the processors. Many supercomputer compilers also come with a large set of pre-defined proprietary vector and matrix functions for many basic tasks. These vectorized functions offer the best way to achieve maximum throughput.

Obviously, the massive parallel processing of, say, a Cray is not possible on most PCs with their one and only CPU. (Even high-end workstations feature still modest 2 or 4-CPU configurations.) Consequently, at first sight, it might seem useless to apply the principle of vectorized programming to the PC. Actually, however, there are many vector-specific optimizations possible, even for computers with only one CPU. Most of these optimizations are not available to present compilers. Rather, one has to go down to the machine-code level. Hand-optimized, Assembler-written vector functions outperform compiled loops by a factor of two to three, on the average. This means that vectorization, properly done, is indeed worth the effort, also for PC programs.

Here are the most important optimization strategies, employed in OptiVec to boost the performance:

Prefetch of chunks of vector elements
Beginning with the Pentium III processor, Intel introduced the very useful feature of explicit memory prefetch. With these commands, it is possible to "tell" the processor to fetch data from memory sufficiently in advance, so that no time is waisted waiting for them when they are actually needed.

Cache control
The Pentium III+ processors offer the possibility to mark data as "temporal" (will be used again) or "non-temporal" (used only once), while they are fetched or stored. In OptiVec functions, it is assumed that input vectors (and matrices) will not be used again, whereas the output vectors are likely to become the input for some ensuing procedure. Consequently, the cache is bypassed while loading input data, but the output data are written into the cache. Of course, this approach breaks down if the vectors or matrices become too large to fit into the cache. For these cases, a large-vector version of the OptiVec libraries is available which bypasses the cache also while writing the output vectors. For simple arithmetic functions, up to 20% in speed are gained as compared to the small-and-medium-size version. On the other hand, as this large-vector version effectively switches the cache off, a drastic performance penalty (up to a factor of three or four!) will result, if it is used for smaller systems. For the same reason, you should carefully check if your problem could perhaps be split up into smaller vectors, before resorting to the large-vector version. This would allow to achieve the much higher performance resulting from efficient data caching.

Use of SIMD commands
You might wonder why this strategy is not listed first. The SSE or "Streaming Single-Instruction-Multiple-Data Extensions" of Pentium III and Pentium 4 provide explicit support for vectorized programming with floating-point data in float / single or double precision (the latter only for Pentium 4). At first sight, therefore, they should revolutionize vector programming. Given today's processor and data bus speeds, however, many of the simple arithmetic operations have become data transfer limited, and the use of SIMD commands does not make the large difference (with respect to well-written FPU code) it could make otherwise. In most cases, the advantage of treating four floats in a single command melts down to a 20-30% increase in speed (which is not that bad, anyway!). For more complicated operations, on the other hand, SIMD commands often cannot be employed, either because conditional branches have to be taken for each vector element individually, or because the "extra" accuracy and range, available by traditional FPU commands (with their internal extended accuracy), allows to simplify algorithms so much that the FPU code is still faster. As a consequence, we use SIMD commands only where a real speed gain is possible without affecting the accuracy.

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 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/PentiumXX 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. In connection with data-prefetching, described above, the depth of the unrolled loops is most often adapted to the cache line size of 32 bytes.

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 – Borland Pascal/Delphi even into 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. Additionally, you can call V_setFPAccuracy( 1 ); to actively switch the FPU to single precision, if that is enough for a given application. Thereby, execution can be significantly sped up from Pentium CPUs on. For details and precautions, see V_setFPAccuracy.

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 loading the parameters onto the stack.

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. Many OptiVec functions need double-precision or extended-precision real local variables on the stack (mainly for integer/floating-point conversions or for range checking). 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. Consequently, those OptiVec functions where this is an issue, use special procedures to align their local variables on 8-byte (for doubles) or 16-byte boundaries (for extendeds).

Unprotected and reduced-range functions
OptiVec offers alternative forms of some mathematical functions, where you have the choice between the fully protected variant with error handling and another, unprotected variant without. In the case of the integer power functions, for example, the absence of error checking allows the unprotected versions to be vectorized much more efficiently. Similarly, the sine and cosine functions can be coded more efficiently for arguments that the user can guarantee to lie in the range -2p and +2p. In these special cases, the execution time may be reduced by up to 40%, depending on the hardware environment. This increased speed has always to be balanced against the increased risk, though: If any input element outside the valid range is encountered, the unprotected and reduced-range functions will crash without warning.

Multithread support
All the above being said about single-CPU PCs, there are high-end workstations and servers on the market, equipped with 2 or 4 PentiumXX chips. While multi-tasking and multi-threading is possible also on single-CPU PCs, multi-processor configurations allow the operating system to distribute threads among the available processors, doubling or quadrupling the overall performance. For that, any functions running in parallel must be prevented from interfering with each other through read/write operations on global variables. 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. 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.
Be extremely careful with multi-threading, if you are using the P6 or P7 versions of OptiVec: The earlier releases of 32-bit Windows do not save the XMM registers (employed in the SIMD commands) during task switches.

Back to Table of Contents

1.2 Licence Terms for the Shareware Version

The following licence terms apply to the Shareware version of OptiVec. For the licence terms of the Registered version, please see paragraph 1.3.

This is the Shareware version of OptiVec ("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.
  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. After the end of the trial period, they will cease functioning.
  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.
  5. 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.
  6. 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.
  7. You may not decompile, disassemble, or otherwise reverse engineer the SOFTWARE into a machine-readable form. You may, however, inspect the functions it contains by means of debuggers like those included in the Borland and Microsoft compilers.
Copyright for the SOFTWARE and its documentation © 1996-2004 OptiCode – Dr. Martin Sander Software Dev.
All rights reserved, including those of translation into foreign languages.

Back to Table of Contents

1.3 Registered Versions

1.3.1 Registered Versions: Ordering

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 are needed for your customers! Corporate site and world-wide licences are available upon request.

The full versions (both the commercial and the educational editions) of OptiVec

a) International Orders: Pricing

OptiVec for Borland C/C++,  Microsoft Visual C++,  or Borland Delphi
(CD-ROM, printed handbook available for the C/C++ versions in English or German)

$    99 for 1 unit of the educational edition 
$  249 for 1 unit of the commercial edition
$  699 for 5 units (only through SWREG)
$ 1199 for 10 units (only through SWREG)
Add $ 5 for S&H (postal) and applicable VAT.

CMATH for Borland C/C++,  Microsoft Visual C++,  or Borland Delphi separately
(CD-ROM, manual in English)

$   39 for 1 unit of the educational edition
$   60 for 1 unit of the commercial edition
$ 200 for 5 units (only through SWREG)
$ 350 for 10 units (only through SWREG)
Add $ 5 for S&H (postal) and applicable VAT.

OptiVec for Borland (Turbo) Pascal

$ 35 for 1 unit + $ 5 for S&H

CMATH for Borland (Turbo) Pascal separately

$ 25 for 1 unit + $ 5 for S&H

International: Ordering Options

For your protection, we handle credit-card orders through two specialized secure services: 

SWREG:
When ordering online through SWREG, please use the product-specific links below:
OptiVec for C/C++
OptiVec for Pascal/Delphi
Please choose the exact version and delivery options in the simple pulldown menu on the respective page.

ShareIt:
When ordering online through ShareIt, please use the product-specific links below:
OptiVec for Borland C/C++ (English)
OptiVec für Borland C/C++ (Deutsch)
CMATH for Borland C/C++
OptiVec for Microsoft Visual C++
CMATH for Microsoft Visual C++
OptiVec for Borland Delphi
CMATH for Borland Delphi
OptiVec for Borland (Turbo) Pascal
CMATH for Borland (Turbo) Pascal

You may also order by e-mail to register@shareit.com.
US customers can also contact ShareIt! by telephone 1-724-850-8186 or FAX 1-724-850-8189 (only for orders, please).
Note the program No.: 
commercial  educational
OptiVec for Borland C/C++ (English)101557102654
OptiVec für Borland C/C++ (Deutsch) 101556149813
CMATH for Borland C/C++101353102655
OptiVec for Microsoft Visual C++103421149811
CMATH for Microsoft Visual C++103422103441
OptiVec for Borland (Turbo) Pascal103423 
CMATH for Borland (Turbo) Pascal103424 
OptiVec for Borland Delphi103443103859
CMATH for Borland Delphi103844103860

b) Orders in the European Union

If you can pay in Euro and order directly from the author, the pricing is

OptiVec for Borland C/C++,  Microsoft Visual C++,  or Borland Delphi
(CD-ROM, printed handbook available for the C/C++ versions in English or German)

EUR    89 for 1 unit of the educational edition
EUR  199 for 1 unit of the commercial edition
EUR  649 for 5 units
EUR 1199 for 10 units
(incl. 16% German VAT, plus EUR 5,- handling charge).

CMATH for Borland C/C++,  Microsoft Visual C++,  or Borland Delphi separately
(CD-ROM, manual in English)

EUR   39 for 1 unit of the educational edition
EUR   59 for 1 unit of the commercial edition
EUR 199 for 5 units
EUR 349 for 10 units
(incl. 16% German VAT, plus EUR 5,- handling charge).

OptiVec for Borland (Turbo) Pascal

EUR 29 for 1 unit + EUR 5 for S&H (incl. 16% German VAT)

CMATH for Borland (Turbo) Pascal separately

EUR 19 for 1 unit + EUR 5 for S&H (incl. 16% German VAT)

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

Please send a print-out of this order form to

OptiCode – Dr. Martin Sander Software Dev.
Steinachstr. 9A
D-69198 Schriesheim
Germany

FAX +49 - 6203 - 601 733

For any other questions related to ordering OptiVec, please contact us at: sales@optivec.com

Back to Table of Contents

1.3.2 License Terms for the Registered version

If you got this file with the registered version of OptiVec, these are the license terms valid for you:

This is a single copy license for OptiVec ("SOFTWARE"), granted by OptiCode – Dr. Martin Sander Software Development ("OptiCode").

The SOFTWARE in this package is licensed to you as the user. It is not sold. The term "user" means a programmer who links binary code of this SOFTWARE into his own applications. Those people using, in turn, his applications without the need of installing this SOFTWARE themselves, do not need any runtime license for the SOFTWARE. The right to distribute applications containing code of this SOFTWARE is included in the license fee for the commercial version.

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 the Borland and Microsoft compilers.
  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" and on condition that the functions of this SOFTWARE are permanently linked into a program etc., but do not appear as a library to the user of that application.
  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. OptiCode's liability is limited by the enclosed Limited Warranty. In no case shall OptiCode's liability exceed the license paid for the right to use the SOFTWARE.

Limited Warranty for the Registered version
  1. OptiCode 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, of fitness for a particular purpose, or of noninfringement are limited to the terms of the above express warranties.
  6. OptiCode 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 © 1996-2004 OptiCode – Dr. Martin Sander Software Development. All rights reserved.

Back to Table of Contents

1.4 Getting Started

To install OptiVec, please follow these steps:
  1. In order to use OptiVec, you need an already installed copy of your C/C++, Delphi, or Pascal compiler. Install OptiVec by executing INSTALL.EXE from the root directory of the installation disk or CD-ROM. Normally, OptiVec will be installed into a directory named "OPTIVEC". This directory holds the documentation.
  2. Include the OptiVec lib and include (C/C++) or units (Pascal/Delphi) subdirectories into the search path.
  3. a) C/C++:
    Assuming your OptiVec directory is C:\OPTIVEC, add
    C:\OPTIVEC\LIB to the library search path and
    C:\OPTIVEC\INCLUDE to the include-file search path of the IDE (and of the configuration file TURBOC.CFG and BCC32.CFG in case you are using Borland's command-line compilers).
     
    b) Pascal/Delphi, 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.
     
    c) Pascal/Delphi, Registered version:
    Pascal: The units for 386+387 will be installed into the directory OPTIVEC\UNITS.
    The additional units for 486/Pentium are contained in the file UNITS4.ZIP and are not automatically installed. If you wish to use them, manually create the subdirectory OPTIVEC\UNITS4 and unzip the file mentioned into it.
    Delphi: The installation routine you have to execute is named after the target Delphi version: INSTALL4.EXE, INSTALL5.EXE, INSTALL6.EXE, etc. The units (.DCU files) for 486 will be installed into OPTIVEC\LIB3, those for 486 into OPTIVEC\LIB4, those for Pentium III+ into OPTIVEC\LIB6, and the large-vector version of the latter into OPTIVEC\LIB6L. Likewise, the units for Pentium 4 go into OPTIVEC\LIB7 and OPTIVEC\LIB7L.
  4. Choose the desired platform, target, and configuration:
    1. Borland C++:
      Choose the desired platform (DOS, Windows3.x, or Win32) and memory model. Select the required library from the following table (add the suffix .LIB) and add it to your project.
      The Shareware versions contain only libraries for either DOS-LARGE, Windows-LARGE, or Win32.
      Borland C++ 16-bit models only:
      If you wish to use MatrixLib functions, you must additionally include another library in your project list, whose name is derived from the names in the table by replacing the leading "V" by "M". For example, for DOS-LARGE, 386, you would need VCL3.LIB and MCL3.LIB.
      Likewise, if you wish to use CMATH functions, you have to add the CMATH library, whereby the letter "V" is replaced by "CMATH". For the above example, this would be CMATHL3.LIB.
       
      PlatformMemory ModelRequired Processor
        
      286386+387486DX/PentiumPentium IIIPentium 4
      DOSTINY
      VCS2VCS3VCS4  
      DOSSMALL
      VCS2VCS3VCS4  
      DOSMEDIUM
      VCM2VCM3VCM4  
      DOSCOMPACT
      VCC2VCC3VCC4  
      DOSLARGE
      VCL2VCL3VCL4  
      DOSHUGE
      VCH2VCH3VCH4  
      WindowsSMALL
      VCS2WVCS3WVCS4W  
      WindowsMEDIUM
      VCM2WVCM3WVCM4W  
      WindowsCOMPACT
      VCC2WVCC3WVCC4W  
      WindowsLARGE
      VCL2WVCL3WVCL4W  
      32-bit Windows
      static run-time library
      dynamic run-time library
      (FLAT)
      GUI or Console
       
      VCF3W
      VCF3WD
       
      VCF4W
      VCF4WD
       
      VCF6W
      VCF6WD
       
      VCF7W
      VCF7WD
      32-bit Windows
      static run-time library
      dynamic run-time library
      (FLAT)
      Large-Vector version
       
       
       
       
       
       
       
      VCF6L
      VCF6LD
       
      VCF7L
      VCF7LD
       
    2. Visual C++: Choose the target and configuration and add the corresponding OptiVec library to your project, according to the following table. Note that the Shareware version contains only the 486DX/Pentium libraries for single-thread debug and multi-thread debug.
       
      TargetConfiguration486/Pentium Pentium IIIPentium III,
      Large-vector version
      Pentium IIIPentium 4,
      Large-vector
      single-threaddebugOVVCSD.LIBOVVCSD6.LIBOVVCLSD6.LIBOVVCSD7.LIBOVVCLSD7.LIB
      single-threadreleaseOVVCSR.LIBOVVCSR6.LIBOVVCLSR6.LIBOVVCSR7.LIBOVVCLSR7.LIB
      multi-threaddebugOVVCMTD.LIBOVVCMTD6.LIBOVVCLTD6.LIBOVVCMTD7.LIBOVVCLTD7.LIB
      multi-threadreleaseOVVCMTR.LIBOVVCMTR6.LIBOVVCLTR6.LIBOVVCMTR7.LIBOVVCLTR7.LIB
      multi-thread DLLdebugOVVCMDD.LIBOVVCMDD6.LIBOVVCLDD6.LIBOVVCMDD7.LIBOVVCLDD7.LIB
      multi-thread DLLreleaseOVVCMDR.LIBOVVCMDR6.LIBOVVCLDR6.LIBOVVCMDR7.LIBOVVCLDR7.LIB
       
      In order to allow OptiVec to be used in applications both with and without MFC, it calls the Windows API only directly, not via MFC. However, if you use MFC (either as a static library or as a DLL), Visual C++ does not automatically link the import library, user32.lib. You have to explicitly do this yourself: The line, Project / Settings / Linker / Object and Library Modules must contain user32.lib. Otherwise you would get the linker error "error LNK2001: Unresolved external symbol __imp__MessageBoxA@??".
       
    3. Borland Pascal:
      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.
       
    4. Borland Delphi:
      No choices are to be made at this level.
  5. Declare the use of OptiVec functions in your program:
  6. Borland C/C++ 16-bit programs only:
  7. Have a look into the sample programs:
After these preparations, all OptiVec functions are available for your programs.
Should you wish to remove OptiVec from your computer, please run UNINSTAL.EXE or simply delete the directory OPTIVEC with its subdirectories.

Back to Table of Contents


2. Elements of OptiVec Routines

2.1 Synonyms for Some Data Types

To increase the versatility and completeness of OptiVec, additional data types are defined in <VecLib.h> or the unit VecLib:

a) C/C++ only:

The data type ui (short for "unsigned index") is used for the indexing of vectors and is defined as "unsigned int". Only in the HUGE model (supported in the registered version of OptiVec for Borland C++), ui is defined as "unsigned long", in order to correctly address huge arrays (greater than 64 kBytes, but with 16-bit addressing).

The 64-bit integer data type (__int64 in BC++ Builder and MS Visual C++, Int64 in Delphi, Comp in Turbo Pascal) is called quad (for "quadword integer") in OptiVec.
The type quad is always signed. At present, OptiVec does not offer functions for unsigned 64-bit integers.

The data type extended, which is familiar to Pascal/Delphi programmers, is defined as a synonym for "long double" in OptiVec for C/C++. As Visual C++ does not support 80-bit reals, we define "extended" as "double" in the OptiVec versions for that compiler.
The reason for the choice of the name "extended" is that all OptiVec routines shall have identical names in C/C++ and Pascal/Delphi 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. While the letter "L" (which could possibly stand for "long double") is already overcrowded by the data types long int and unsigned long, the letter "E" is unique to the data type extended and therefore used in the prefixes for vectors and functions of long double precision. This way, the letters defining the real- number data types are 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".

b) Pascal/Delphi only:

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. As noted above, possible future 128-bit and 256-bit real numbers 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/C++ versions of OptiVec, we define those synonyms present in Delphi (and a few more) also for 16-bit Pascal, as described in the following table:  
typePascal namesynonymderived prefix
8 bit signedShortIntByteIntVBI_
8 bit unsignedByteUByteVUB_
16 bit signed SmallInt VSI_
16 bit unsigned WordUSmallVUS_
32 bit signed LongIntVLI_
32 bit unsigned  ULongVUL_
64 bit signed CompQuadIntVQI_
16/32 bit signedInteger VI_
16/32 bit unsignedCardinalUIntVU_

Turbo Pascal only:
The unsigned 32-bit integer type ULong is treated as such only inside OptiVec 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. Delphi 4+ always treats ULong as such.
QuadInts are always signed. As yet, there is nothing like a "UQuad".

Delphi:
As Delphi supports 64-bit integers, QuadInt is not defined as Comp, but is used as a synonym for the 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.

Back to Table of Contents

2.2 Complex Numbers:
The Data Types fComplex, dComplex, eComplex, fPolar, dPolar, and ePolar

Complex numbers are treated in C/C++ in quite a confusing way. ANSI C offers only a struct complex, Borland's C/C++ compiler additionally a struct _complexl for complex numbers of double and long double precision, resp. The real and imaginary parts are denoted as x and y.
From early versions on, Borland C++ has also offered a class complex which is of double precision; the real and imaginary parts are accessible via the functions real and imag. There is also a number of mathematical functions available for this class.
Finally, the Standard C++ library, included from Borland C++ 5 and Visual C++ 6 on, offers the classes complex<float>, complex<double>, and complex<long double>, equipped with basic functionality and the same range of mathematical functions as offered by the class complex.
Later versions of Delphi offer a unit Complex which treats complex number as variant types – with all the inefficiencies associated with such an implementation.
Functions in polar coordinates are not available in any of these approaches.

Most compilers and available libraries implement complex functions very inefficiently and inaccurately. (Just writing down the textbook formula for a complex function, like it is usually done, works fine only for a very limited range of arguments!)

Our aims are

To this end, the complex math library CMATH was created and is included in OptiVec. CMATH is described in greater detail in the file CMATH.HTM. If you use any of the non-vectorized functions contained in CMATH with C/C++, you should include <newcplx.h> (for C++ modules) or <cmath.h> (for plain-C modules) before (!) any of the VectorLib include files.

VectorLib itself contains the necessary initialization functions of complex numbers and all vectorized forms of complex math functions. If you are using only these, you need not explicitly include CMATH. In this case, the following complex data types are defined in <VecLib.h> for C/C++:
typedef struct { float Re, Im; } fComplex;
typedef struct { double Re, Im; } dComplex;
typedef struct { extended Re, Im; } eComplex;
typedef struct { float Mag, Arg; } fPolar;
typedef struct { double Mag, Arg; } dPolar;
typedef struct { extended Mag, Arg; } ePolar;

(the data type extended is used as a synonym for long double, see above.)

The corresponding definitions for Pascal/Delphi are contained in the unit VecLib:
type fComplex = record Re, Im: Float; end;
type dComplex = record Re, Im: Double; end;
type eComplex = record Re, Im: Extended; end;
type fPolar = record Mag, Arg: Float; end;
type dPolar = record Mag, Arg: Double; end;
type ePolar = record Mag, Arg: 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 either by setting the constituent parts separately to the desired value, e.g.,
z.Re = 3.0; z.Im = 5.7;
p.Mag = 4.0; p.Arg = 0.7;

(of course, the assignment operator is := in Pascal/Delphi).
Alternatively, the same initialization can be accomplished by the functions fcplx or fpolr:
C/C++:
z = fcplx( 3.0, 5.7 );
p = fpolr( 4.0, 0.7 );

Pascal/Delphi:
fcplx( z, 3.0, 5.7 );
fpolr( p, 3.0, 5.7 );

For double-precision complex numbers, use dcplx and dpolr, for extended-precision complex numbers, use ecplx and epolr.
Pointers to arrays or vectors of complex numbers are declared using the data types cfVector, cdVector, and ceVector (for cartesian complex) and pfVector, pdVector, and peVector (for polar complex) described below.

Back to Table of Contents

2.3 Vectors and Arrays:
The Data Types fVector, dVector, eVector,
cfVector, cdVector, ceVector, pfVector, pdVector, peVector,
iVector, biVector, siVector, liVector, qiVector,
uVector, ubVector, usVector, ulVector, and uiVector

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 declared in <VecLib.h> or the unit VecLib. In contrast to the fixed-size static arrays, the VectorLib types use dynamic memory allocation and allow for varying sizes. Because of this increased flexibility, we recommend that you predominantly use the latter. Here they are:
 
C/C++
typedeffloat *fVector
typedefdouble *dVector
typedefextended *eVector
typedeffComplex *cfVector
typedefdComplex *cdVector
typedefeComplex *ceVector
typedeffPolar *pfVector
typedefdPolar *pdVector
typedefePolar *peVector
typedefint *iVector
typedefbyte *biVector
typedefshort *siVector
typedeflong *liVector
typedefquad *qiVector
typedefunsigned *uVector
typedefunsigned byte *ubVector
typedefunsigned short *usVector
typedefunsigned long *ulVector
typedefui *uiVector
  Pascal/Delphi
typefVector= ^Float;
typedVector= ^Double;
typeeVector= ^Extended;
typecfVector= ^fComplex;
typecdVector= ^dComplex;
typeceVector= ^eComplex;
typepfVector= ^fPolar;
typepdVector= ^dPolar;
typepeVector= ^ePolar
typeiVector= ^Integer;
typebiVector= ^ByteInt;
typesiVector= ^SmallInt;
typeliVector= ^LongInt;
typeqiVector= ^QuadInt;
typeuVector= ^UInt;
typeubVector= ^UByte;
typeusVector= ^USmall;
typeulVector= ^ULong;

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".
 
Note: in connection with Windows programs, often the letter "l" or "L" is used to denote "long int" variables. In order to prevent confusion, however, the data type "long int" is signalled by "li" or "LI", and the data type "unsigned long" is signalled by "ul" or "UL". Conflicts with prefixes for "long double" vectors are avoided by deriving these from the alias name "extended" and using "e", "ce", "E", and "CE", as described above and in the following.
 
C/C++ specific:
Vector elements can be accessed either with the [] operator, like VA[375] = 1.234;
or by the type-specific functions VF_element (returns the value of the desired vector element, but cannot be used to overwrite the element) and VF_Pelement (returns the pointer to a vector element). The latter function may be used to set vector values, e.g.
*VF_Pelement( X, 3 ) = 5.7;
Especially for some older Borland C versions (which have a bug in the pointer-arithmetics), VF_Pelement has to be used instead of the syntax X+n.
In your programs, you may mix these vector types with the static arrays of classic C style.
For example:
float a[100]; /* classic static array */
fVector b=VF_vector(100); /* VectorLib vector */
VF_equ1( a, 100 ); /* set the first 100 elements of a equal to 1.0 */
VF_equC( b, 100, 3.7 ); /* set the first 100 elements of b equal to 3.7 */

Pascal/Delphi specific:
The elements of OptiVec vectors cannot be accessed with the [] operator here. Instead, the the type-specific functions VF_element (returns the value of the desired vector element, but cannot be used to overwrite the element) and VF_Pelement (returns the pointer to a vector element) have to be used. The latter function allows to set vector values, e.g.
VF_Pelement( X, 3 )^ := 5.7;
As in C/C++, you may mix the OptiVec vector types with the static arrays of classic Pascal style. Static arrays have to be passed to OptiVec functions with the "address of" operator. Here, the above example reads:
a: array[0..99] of Single; (* classic static array *)
b: fVector;(* VectorLib vector *)
b := VF_vector(100);
VF_equ1( @a, 100 ); (* set first 100 elements of a = 1.0 *)
VF_equC( b, 100, 3.7 ); (* set first 100 elements of b = 3.7 *)

Since Version 4, Delphi has also offered dynamically-allocated arrays, which may also be used as arguments for OptiVec functions. The following table compares the pointer-based vectors of VectorLib with the array types of Pascal/Delphi:
 
 OptiVec vectorsPascal/Delphi static/dynamic arrays
alignment of first elementon 32-byte boundary for optimum cache-line matching2 or 4-byte boundary (may cause line-break penalty for double, QuadInt)
alignment of following elementspacked (i.e., no dummy bytes between elements, even for 10- and 20-bit typesarrays must be declared as "packed" for Delphi to be compatible with OptiVec
index range checkingnoneautomatic with built-in size information
dynamic allocationfunction VF_vectorVF_vector0procedure SetLength (Delphi only)
initialization with 0optional by calling VF_vector0always (Delphi only)
de-allocationfunction V_freeV_freeAllprocedure Finalize (Delphi only)
reading single elementsfunction VF_element:
a := VF_element(X,5);
Delphi only: typecast into array also possible:
a := fArray(X)[5];
index in brackets:
a := X[5];
setting single elementsfunction VF_Pelement:
VF_Pelement(X,5)^ := a;
Delphi only: typecast into array also possible:
fArray(X)[5] := a;
index in brackets:
X[5] := a;
passing to OptiVec functiondirectly:
VF_equ1( X, sz );
address-of operator:
VF_equ1( @X, sz );
passing sub-vector to OptiVec functionfunction VF_Pelement:
VF_equC( VF_Pelement(X,10), sz-10, 3.7);
address-of operator:
VF_equC( @X[10], sz-10, 3.7 );
 
Summarizing the properties of OptiVec vectors and of Pascal/Delphi arrays, the latter are somewhat more convenient and, due to the index range checking, safer, whereas the pointer-based OptiVec vectors are processed faster (due to the better alignment and to the absence of checking routines).

Back to Table of Contents

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

OptiVec supports the three floating-point data types that are used by the coprocessors of the 80x87 family and the FPU units integrated into the 486DX and Pentium processors and their successors: float, double, and extended (i.e., long double). BCD numbers are not supported.

Any of the algebraic and mathematical functions included in this library 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 (Pascal: Single), VD_ stands for the data type double, and VE_ for the data type extended, i.e., long double. (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. Except for a very few cases, 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 long double).

One partial exception from this rule comes from the fact that floating-point return values of OptiVec functions are returned in extended precision on the number stack. Therefore, you may assign the return value of a 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 (i.e., as a long double), before writing the line
eProd = VF_prod( X, size );

Borland C++ only:
To use this possibility, you must switch the option "Fast floating point" on (in the IDE in the menu "Options/Compiler/Advanced Code Generation", or the command-line compiler option "-ff"),

For the description of the functions in the Alphabetical Reference, generally only the VF_ version is described and its syntax explicitly given. The versions for the data types double and long double 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.

Back to Table of Contents

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

Any prefix with its second letter being "C" denotes a function of cartesian complex numbers. If the second letter is "P", the function is for complex numbers in polar coordinates. 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, cfVector and float). Similarly, VCD_ is used for double-precision calculations, and VCE_ for extended precision. Wherever "fComplex", "cfVector", and "float" appear in the description of a function in the VCF_ version, the VCD_ and VCE_ versions are obtained by substituting with "dComplex", "cdVector" and "double" or "eComplex", "ceVector", and "extended" (or "long double"), respectively. Likewise, the VPF_,  VPD_, and VPE_ functions work on vector elements of the types fPolar, dPolar, and ePolar, resp.

Return values of the complex data types are not possible in Pascal/Delphi. Therefore, the syntax of those functions returning a complex number is different in C/C++ and Pascal/Delphi.

In contrast to the carelessness with which complex mathematical functions are often treated (see above), the complex functions of OptiVec are designed in such a way as to achieve full accuracy over the complete range of input/output values possible with the respective data type.

In order to perform non-vectorized complex operations with the same level of speed and reliability as the vectorized ones, use CMATH. See CMATH.HTM for details.

Back to Table of Contents

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

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 int, VBI_ stands for byte-sized int, VSI_ for short int, VLI_ for long int and VQI_ for quad integers. VU_ denotes operations with unsigned integers, VUB_ with unsigned byte, VUS_ with unsigned short and VUL_ is the prefix for functions of unsigned long arguments.
C/C++ only: For operations on index-arrays, functions with the prefix VUI_ allow to perform calculations using arguments of the data type ui defined above. The VUI_ versions are always defined as macros, and the compiler automatically substitutes either the VU_ or the VUL_ version, whichever is appropriate for the memory model actually used. In Pascal/Delphi, no VUI_ functions are defined.

Don't be afraid of so many data types. It is one of the advantages of modern computer languages to have them, and it is one of the disadvantages, at the same time, that a programming style is supported which mixes all the data types until it is no longer clear "who is who". In all normal cases, the VI_,  VLI_, and VU_ functions should be sufficient; but keep in mind that there are more available in case you need them.

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" and "fVector" into the desired data type.

Back to Table of Contents

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 unsigned long) is transformed into a dVector (a vector whose elements are doubles).
The type-independent functions are declared in <VecLib.h> and <Vgraph.h> or the units VecLib and Vgraph, respectively. The data-type interconversion functions are declared in the include-files or units belonging to the destination type (i.e. the type into which the numbers are converted).

Back to Table of Contents


3. VecObj, theObject-Oriented Interface for VectorLib

VecObj, the object-oriented C++ interface to OptiVec vector functions was written by Brian Dale, Case Western Reserve University. MatObj, the extension for matrices, is now included as well.
Among the advantages VecObj offers are the following: There are a few draw-backs, though, which you should be aware of: VecObj is contained in the include-files <VecObj.h>, <fVecObj.h>, <dVecObj.h> etc., with one include-file for each of the data-types supported in OptiVec.
To get the whole interface (for all data types at once),
#include <OptiVec.h>.
For access to any of the vector graphics functions, always include <OptiVec.h>.

MS Visual C++ and Borland C++ Builder (but not previous Borland C++ versions): Programmers should put the directive
"using namespace OptiVec;"
either in the body of any function that usestVecObj, or in the global declaration part of the program. Placing the directive in the function body is safer, avoiding potential namespace conflicts in other functions.
The vector objects are defined as classes vector<T>, encapsulating the vector address (pointer) and size.
For easier use, these classes got alias names fVecObj, dVecObj, and so on, with the data-type signalled by the first one or two letters of the class name, in the same way as the vector types described above.

All functions defined in VectorLib for a specific vector data-type are contained as member functions in the respective tVecObj class.
The constructors are available in four forms:
vector(); // no memory allocated, size set to 0
vector( ui size ); // vector of size elements allocated
vector( ui size, T fill ); // as before, but initialized with value "fill"
vector( vector<T> init ); // creates a copy of the vector "init"

For all vector classes, the arithmetic operators
+    -    *    /    +=    -=    *=    /=
are defined, with the exception of the polar-complex vector classes, where only multiplications and divisions, but no additions or subtractions are supported. These operators are the only cases in which you can directly assign the result of a calculation to a vector object, like
fVecObj Z = X + Y; or
fVecObj Z = X * 3.5;
Note, however, that the C++ class syntax rules do not allow a very efficient implementation of these operators. The arithmetic member functions are much faster. If speed is an issue, use
fVecObj Z.addV( X, Y ); or
fVecObj Z.mulC( X, 3.5 );
instead of the operator syntax.
The operator * refers to element-wise multiplication, not to the scalar product of two vectors.

All other arithmetic and math functions can only be called as member functions of the respective output vector as, for example, Y.exp(X). Although it would certainly be more logical to have these functions defined in such a way that you could write "Y = exp(X)" instead, the member-function syntax was chosen for efficiency considerations: The only way to implement the second variant is to store the result of the exponential function of X first in a temporary vector, which is then copied into Y, thus considerably increasing the work-load and memory demands.

While most VecObj functions are member functions of the output vector, there is a number of functions which do not have an output vector. In these cases, the functions are member functions of an input vector.
Example: s = X.mean();.

If you ever need to process a VecObj vector in a "classic" plain-C VectorLib function (for example, to process only some part of it), you may use the member functions
getSize() to retrieve its size,
getVector() for the pointer (of data type tVector, where "t" stands for the usual type prefix), and
Pelement( n ) for a pointer to the to the n'th element.

The syntax of all VecObj functions is described in FUNCREF.HTM together with the basic VectorLib functions for which tVecObj serves as a wrapper.

Back to Table of Contents


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, float a[100];) 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.

The following functions manage dynamically allocated vectors: 
VF_vectormemory allocation for one vector
VF_vector0memory allocation and initialization of all elements with 0
V_freefree one vector
V_nfree free n vectors (only for C, not for Pascal)
V_freeAllfree all existing vectors
 
You should always take proper care to de-allocate the memory of vectors which are no longer needed. 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.
You might wonder why we add still more memory allocation functions to the already rich omnium gatherum of C/C++ and Pascal/Delphi. The reason is that, for every environment and every memory model, the most appropriate memory management functions shall be selected automatically. This means that you, the user, need not deal yourself with the various methods, but can leave this task to OptiVec. Moreover, this makes your programs more easily portable.

Performance tips:

The following functions are used to initialize or re-initialize vectors that have already been created: 
VF_equ0set all elements of a vector equal to 0
VF_equ1set all elements equal to 1
VF_equm1set all elements equal to -1
VF_equCset all elements equal to a constant C
VF_equVmake one vector a copy of another
VFx_equV"expanded" version of the equality operation: Yi = a * Xi + b
VF_ramp"ramp": Xi = a * i + b.
VF_randomhigh-quality random numbers
VF_noisewhite noise
VF_comb"comb": equals a constant C at equidistant points, elsewhere 0
 
The following functions generate windows for use in spectral analysis: 
VF_HanningHanning window
VF_ParzenParzen window
VF_WelchWelch window
 
Complex vectors may be initialized by these functions: 
VF_ReImtoCmerge two vectors, Re and Im, into one cartesian complex vector
VF_RetoCoverwrite the real part of a cartesian complex vector
VF_ImtoCoverwrite the imaginary part of a cartesian complex vector
VF_PolartoCconstruct a cartesian complex vector from polar coordinates, entered as separate vectors Mag and Arg
VF_MagArgtoPmerge two vectors, Mag and Arg into one polar complex vector
VF_MagArgtoPrincipalmerge two vectors, Mag and Arg into one polar complex vector, reducing the Arg range to the principal value, -p < Arg <= +p
VF_MagtoPoverwrite the Mag part of a polar complex vector
VF_ArgtoPoverwrite the Arg part of a polar complex vector
VF_ReImtoPconstruct a polar complex vector from cartesian coordinates, entered as separate vectors Re and Im

Back to Table of Contents

4.2 Index-oriented Manipulations

VF_revreverse the element ordering
VF_reflectset the upper half of a vector equal to the reversed lower half
VF_rotaterotate the ordering of the elements
VF_insertinsert one element into a vector
VF_deletedelete one element from a vector
VF_sortfast sorting of the elements (ascending or descending order)
VF_sortindsorting of an index array associated with a vector
VF_subvectorextract a subvector from a (normally larger) vector, using a constant sampling interval.
VF_indpickfills a vector with elements "picked" from another vector according to their indices.
VF_indputdistribute 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_subvector_equC Xi*samp = C,   i=0,...subsize-1
VF_subvector_addC Xi*samp += C,   i=0,...subsize-1
VF_subvector_subC Xi*samp -= C,   i=0,...subsize-1
VF_subvector_subrC Xi*samp = C - Xi*samp,   i=0,...subsize-1
VF_subvector_mulC Xi*samp *= C,   i=0,...subsize-1
VF_subvector_divC Xi*samp /= C,   i=0,...subsize-1
VF_subvector_divrC Xi*samp = C / Xi*samp,   i=0,...subsize-1
VF_subvector_equV Yi*samp = Yi,   i=0,...subsize-1
VF_subvector_addV Xi*samp += Yi,   i=0,...subsize-1
VF_subvector_subV Xi*samp -= Yi,   i=0,...subsize-1
VF_subvector_subrV Xi*samp = Yi - Xi*samp,   i=0,...subsize-1
VF_subvector_mulV Xi*samp *= Yi,   i=0,...subsize-1
VF_subvector_divV Xi*samp /= Yi,   i=0,...subsize-1
VF_subvector_divrV Xi*samp = Yi / Xi*samp,   i=0,...subsize-1
 
Searching tables for specific values is accomplished by:
VF_searchC search for the element of a vector that is closest to a pre-set value C (closest, closest larger-or-equal, or closest smaller-or-equal value, depending on a parameter "mode")
VF_searchVthe same, but for a whole array of pre-set values
 
Interpolations are performed by: 
VF_polyinterpolpolynomial interpolation
VF_ratinterpolrational interpolation
VF_splineinterpolcubic spline interpolation
 

Back to Table of Contents

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. Only a few examples are given; the rest should be obvious: 
V_FtoDfloat to double
V_CDtoCFcomplex<double> to complex<float> (with overflow protection)
V_PFtoPEpolar<float> to polar<extended>
VF_PtoCpolar<float> to complex<float>
V_ItoLIint to long int
V_ULtoUSunsigned long to unsigned short
V_ItoUsigned int to unsigned int. Interconversions between signed and unsigned types can only be performed on the same level of accuracy. Functions like "V_UStoLI" do not exist.
V_ItoFint to float
 
The conversion of floating-point numbers into integers is performed by the following functions, differing in the way a possible fractional part is treated: 
VF_roundtoIround to the closest integer
VF_choptoIround by neglecting ("chopping off") the fractional part
VF_trunctoIthe same as VF_choptoI
VF_ceiltoIround to the next greater-or-equal integer
VF_floortoIround to the next smaller-or-equal integer
 
These operations are treated as mathematical functions and are further described in chapter 4.6.1.

Back to Table of Contents

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 2n, 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 2n had been added to (or subtracted from) the "correct" result as necessary to reach the legal range.
For example, in the data type short / 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 short numbers which is -32768 <= x <= +32767. short / SmallInt is a 16-bit type, so n = 16, and 2n = 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.
The 64-bit data type quad / QuadInt does not employ this implicit modulo-2n arithmetics. Overflow conditions lead to undefined results.

Back to Table of Contents

4.5 Basic Functions of Complex Vectors

The following functions are available for the basic treatment of cartesian complex vectors: 
VF_ReImtoCform a cartesian complex vector out of its real and imaginary parts
VF_RetoCoverwrite the real part
VF_ImtoCoverwrite the imaginary part
VF_CtoReImextract the real and imaginary parts
VF_CtoReextract the real part
VF_CtoImextract the imaginary part
VF_PolartoCform a cartesian complex vector out of polar coordinates, entered as separate vectors Mag and Arg
VF_CtoPolartransform cartesian complex into polar coordinates, returned in the separate vectors Mag and Arg
VF_CtoAbsabsolute value (magnitude of the pointer in the complex plane)
VF_CtoArgargument (angle of the pointer in the complex plane)
VF_CtoNormnorm (here defined as the square of the absolute value)
VCF_normtoCnorm, stored as a cartesian complex vector (with all imaginary parts equal to 0)
 
The corresponding functions for polar coordinates are:
VF_MagArgtoPmerge two vectors, Mag and Arg into one polar complex vector
VF_MagArgtoPrincipalmerge two vectors, Mag and Arg into one polar complex vector, reducing the Arg range to the principal value, -p < Arg <= +p
VF_MagtoPoverwrite the Mag part of a polar complex vector
VF_ArgtoPoverwrite the Arg part of a polar complex vector
VF_PtoMagArgextract the Mag and Arg parts
VF_PtoMagextract the Mag part
VF_PtoArgextract the Arg part
VF_PtoNormnorm (here defined as the square of the magnitude)
VF_ReImtoPconstruct a polar complex vector from cartesian coordinates, entered as separate vectors Re and Im
VF_PtoReImtransform a polar complex vector into two real vectors, representing the corresponding cartesian coordinates Re and Im
VF_PtoRecalculate the real part of the polar complex input numbers
VF_PtoImcalculate the imaginary part of the polar complex input numbers
VPF_principalcalculate the principal value. You might recall that each complex number has an infinite number of representations in polar coordinates, with the angles differing by an integer multiple of 2 p. The representation with -p < Arg <= +p is called the principal value.
 

Back to Table of Contents

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 scalar ANSI C or Pascal functions or derived from them. In C/C++, errors are handled by _matherr and _matherrl. In Pascal/Delphi, OptiVec allows the user to control error handling by means of the function V_setFPErrorHandling.

In addition to this error handling "by element", the return values of the VectorLib math functions show if all elements have been processed successfully. In C/C++, the return value is of the data-type int, in Pascal/Delphi, it is IntBool. (We do not yet use the newly introduced data type bool for this return value in C/C++, in order to make VectorLib compatible also with older versions of C compilers.) If a math function worked error-free, the return value is FALSE (0), otherwise it is TRUE (any non-zero number).

Back to Table of Contents

4.6.1 Rounding

Some of the functions converting floating-point into integer vectors have already been noted above. The result of these rounding operations may either be left in the original floating-point format, or it may be converted into one of the integer types. The following functions store the result in the original floating-point format: 
VF_roundround to the closest integer
VF_chopround by neglecting ("chopping off") the fractional part
VF_truncthe same as VF_chop
VF_ceilround to the next greater-or-equal integer
VF_floorround to the next smaller-or-equal integer
 
The following functions store the result as integers (type int / Integer): 
VF_roundtoIround to the closest integer
VF_choptoIround by neglecting ("chopping off") the fractional part
VF_trunctoIthe same as VF_choptoI
VF_ceiltoIround to the next greater-or-equal integer
VF_floortoIround to the next smaller-or-equal integer
 
The target type may also be any of the other integer data types. A few examples should suffice: 
VF_choptoSIneglect the fractional part and store as short int / SmallInt
VF_ceiltoLIround up and store as long int / LongInt
VF_floortoQIround downwards and store as quadruple integer, quad / QuadInt
VF_roundtoUround and store as unsigned / UInt
VF_ceiltoUSround up and store as unsigned short / USmall
VD_choptoULneglect the fractional part and store as unsigned long / ULong

Back to Table of Contents

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_cmp0compare to 0
VD_cmpCcompare to a constant C
VE_cmpVcompare corresponding vector elements
 
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_eq0check if equal to 0
VD_cmp_gtCcheck if greter than a constant C
VE_cmp_leVcheck if less than or equal to corresponding vector element
 
Alternatively, the indices of the elements for which the answer was "TRUE" may be stored in an index-array, as in:
 
VF_cmp_neCindstore indices of elements not equal to a constant C
VD_cmp_lt0indstore indices of elements less than 0
VE_cmp_geVindstore indices of elements greater than or equal to corresponding vector elements
 
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 check if 0 <= x <= C  (C positive)
or  0 >= x >= C  (C negative)
VF_cmp_exclrange0C check if 0 < x < C  (C positive)
or  0 > x > C  (C negative)
VF_cmp_inclrangeCC check if CLo <= x <= CHi
VF_cmp_exclrangeCC check if CLo < x < CHi
VF_cmp_inclrange0Cind store indices of elements 0 <= x <= C  (C positive)
or  0 >= x > C  (C negative)
VF_cmp_exclrange0Cind store indices of elements 0 < x < C  (C positive)
or  0 > x > C  (C negative)
VF_cmp_inclrangeCCind store indices of elements CLo <= x <= CHi
VF_cmp_exclrangeCCind store indices of elements CLo < x < CHi
 
The following functions test if one or more values can be found in a vector: 
VF_iselementC returns TRUE, if C is an element of a vector
VF_iselementV checks for each element of a vector if it is contained in a table

Back to Table of Contents 

4.6.3 Direct Bit-Manipulation

For the integer data types, a number of bit-wise operations is available, which can be used, e.g., for fast multiplication and divisions by integer powers of 2. 
VI_shlshift the bits to the left
VI_shrshift the bits to the right
VI_orapply a bit mask in an OR operation
VI_xorapply a bit mask in an XOR operation
VI_notinvert all bits

Back to Table of Contents

4.6.4 Basic Arithmetics, Accumulations

As before, 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_negYi = - Xi
VF_absYi = | Xi |
VCF_conjYi.Re = Xi.Re; Yi.Im = -(Xi.Re)
VF_invYi = 1.0 / Xi
 
VF_equCXi = C
VF_addCYi = Xi + C
VF_subCYi = Xi - C
VF_subrCYi = C - Xi
VF_mulCYi = Xi * C
VF_divCYi = Xi / C
VF_divrCYi = C / Xi
VF_modCYi = Xi mod C
VF_equVYi = Xi
VF_addVZi = Xi + Yi
VF_subVZi = Xi - Yi
VF_subrVZi = Yi - Xi
VF_mulVZi = Xi * Yi
VF_divVZi = Xi / Yi
VF_divrVZi = Yi / Xi
VF_modVZi = Xi mod Yi
VF_add2VZi = Xi + Y1i + Y2i
VF_sub2VZi = Xi - Y1i - Y2i
 
Besides these basic operations, several frequently-used combinations of addition and division have been included, not to forget the Pythagoras formula: 
VF_hypCYi = Xi / (Xi + C)
VF_redCYi = (Xi * C) / (Xi + C)
VF_visCYi = (Xi - C) / (Xi + C)
VF_hypotCYi = sqrt( Xi² + C² )
VF_hypVZi = Xi / (Xi + Yi)
VF_redVZi = (Xi * Yi) / (Xi + Yi)
VF_visVZi = (Xi - Yi) / (Xi + Yi)
VF_hypotVZi = 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
VFx_divrV Zi = Yi / (a * Xi + b)
 
The simple algebraic functions exist also in yet another special form, with the result being scaled by some arbitrary 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)
 
Other simple operations include: 
VF_maxCset Yi equal to Xi or C, whichever is greater
VF_minCchoose the smaller of Xi and C
VF_maxVset Zi equal to Xi or Yi, whichever is greater
VF_minVset Zi equal to Xi or Yi, whichever is smaller
VF_limitlimit the range of values
VF_flush0set all values to zero which are below a preset threshold
VF_intfracsplit into integer and fractional parts
VF_mantexpsplit into mantissa and exponent
 
While, in general, all OptiVec functions are for input and output vectors of the same type, the arithmetic functions exist also for mixed-type operations between the floating-point and the integer types. The result is always stored in the floating-point type. Examples are:
 
VF_addVIfVector Z = fVector X + iVector Y
VD_mulVULdVector Z = dVector X * ulVector Y
VE_divrVBIeVector Z = biVector Y / eVector X

Similarly, there exists a family of functions for the accumulation of data in either the same type or in higher-precision data types. Some examples are: 
VF_accVfVector Y += fVector X
VD_accVFdVector Y += fVector X
VF_accVIfVector Y += iVector X
VQI_accVLIqiVector Y += liVector X
 
Additionally, within the floating-point data-types, you can accumulate two vectors at once:  
VF_acc2VfVector Y += fVector X1 + fVector X2
VD_acc2VFdVector Y += fVector X1 + fVector X2

Back to Table of Contents 

4.6.5 Geometrical Vector Arithmetics

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 scalar product of two vectors
VF_xprod cross-product (or vector product) of two vectors
VF_Euclid Euclidean norm

If, on the other hand, two real input vectors X and Y, or one complex input vector XY, define the coordinates of several points in a planar coordinate system, there is a function to rotate these coordinates:
VF_rotateCoordinates counter-clockwise rotation of the input coordinates specified by the vectors X and Y; the result is returned in the vectors Xrot and Yrot.
VCF_rotateCoordinates counter-clockwise rotation of the input coordinates specified by the cartesian complex vector XY; the result is returned in the vector XYrot.

Back to Table of Contents 

4.6.6 Powers

The following functions raise arbitrary numbers to specified powers. While the "normal versions" perform full error handling, the extra-fast "unprotected versions" can be employed in situations where you are absolutely sure that all input elements yield valid results. 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 normal, 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.
 
normal versionunprotected versionoperation
VF_squareVFu_squaresquare
VF_cubicVFu_cubiccubic
VF_quarticVFu_quarticquartic (fourth power)
VF_ipowVFu_ipowarbitrary integer powers
VF_pown.a.fractional powers
VF_powexpn.a.fractional powers, multiplied by exponential function: xrexp(x)
VF_polyVFu_polypolynomial
 
The following group of functions is used to raise specified numbers to arbitrary powers: 
VF_pow10 fractional powers of 10
VF_ipow10 integer powers of 10 (stored as floating-point numbers)
VF_pow2 fractional powers of 2
VF_ipow2 integer powers of 2 (stored as floating-point numbers)
VF_exp exponential function
VF_exp10 exponential function to the basis 10 (identical to VF_pow10)
VF_exp2 exponential function to the basis 2 (identical to VF_pow2)
VF_expArbBase exponential function of an arbitrary base
VF_sqrt square-root (which corresponds to a power of 0.5)
 
All of these functions exist also in the expanded "VFx_" form, like
VFx_square: Yi = (a * Xi + b)² 
The expanded form of the unprotected functions has the prefix VFux_.

The complex-number equivalents are available as well, both for cartesian and polar coordinates. Additionally, two special cases are covered:
 
VCF_powReExpo real, fractional powers of complex numbers
VCF_exptoP takes a cartesian input vector, returning its exponential function in polar coordinates.

Back to Table of Contents 

4.6.7 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_exp exponential function
VF_expc complementary exponential function Yi = 1 - exp[Xi]
VF_expmx2 exponential function of the negative square of the argument,
Yi = exp( -Xi² ). This is a bell-shaped function.
VF_Gauss Gaussian distribution function
VF_erf Error function (Integral over the Gaussian distribution)
VF_erfc complementary error function, 1 - erf( Xi )
VF_powexpn.a.fractional powers, multiplied by exponential function, Xirexp(Xi)
 
The vectorized hyperbolic functions are available as: 
VF_sinh hyperbolic sine
VF_cosh hyperbolic cosine
VF_tanh hyperbolic tangent
VF_coth hyperbolic cotangent
VF_sech hyperbolic secant
VF_cosech hyperbolic cosecant
VF_sech2 square of the hyperbolic secant

Back to Table of Contents 

4.6.8 Logarithms

VF_log10 decadic logarithm (to the basis 10)
VF_log natural logarithm (to the basis e)
VF_ln synonym for VF_log
VF_log2 binary logarithm (to the basis 2)
 
Again, the cartesian-complex equivalents exist as well. The polar-complex versions, however, are special in that their output is always in cartesian coordinates:
VPF_log10toC decadic logarithm (to the basis 10)
VPF_logtoC natural logarithm (to the basis e)
VPF_lntoC synonym for VPF_logtoC
VPF_log2toC binary logarithm (to the basis 2)

As a special form of the decadic logarithm, the Optical Density is made available by a family of functions of which some examples are contained in the following table: 
VF_ODOD = log10( X0/X ) for fVector as input and as output
VF_ODwDarkOD = log10( (X0-X0Dark) / (X-XDark) ) for fVector as input and as output
VUS_ODtoFOD, calculated in float precision for usVector input
VUL_ODtoDOD, calculated in double precision for ulVector input
VQI_ODtoEwDarkOD with dark-current correction, calculated in extended precision for qiVector input

Back to Table of Contents 

4.6.9 Trigonometric Functions

Some of the basic trigonometric functions are available in two variants. The first variant follows the usual rules of error handling for math functions, whereas the second is for situations where you know beforehand that all input arguments will be in the range -2p <= Xi <= +2p. If you choose to employ these extra-fast reduced-range functions, you really have to be absolutely sure about your input vectors, as these functions will crash without warning in the case of any input number outside the range specified above. The reduced-range functions are available only for the sine and cosine, as all other trigonometric functions need error checking and handling anyway, even in this range.
 
VF_sinsine
VFr_sinextra-fast "reduced-range" sine function for -2p <= Xi <= +2p
VF_coscosine
VFr_coscosine for -2p <= Xi <= +2p
VF_sincossine and cosine at once
VFr_sincossine and cosine for -2p <= Xi <= +2p
VF_tantangent
VF_cotcotangent
VF_secsecant
VF_coseccosecant
 
The following functions yield the squares of the trigonometric functions in a more efficient way than by first calculating the basic functions and squaring afterwards: 
VF_sin2sine²
VFr_sin2sine² for -2p <= Xi <= +2p
VF_cos2cosine²
VFr_cos2cosine² for -2p <= Xi <= +2p
VF_sincos2sine² and cosine² at once
VFr_sincos2sine² and cosine² for -2p <= Xi <= +2p
VF_tan2tangent²
VF_cot2cotangent²
VF_sec2secant²
VF_cosec2cosecant²
 
A very efficient way to calculate the trigonometric functions for arguments representable as rational multiples of p (PI) is supplied by the trigonometric functions with the suffix "rpi" (meaning "rational multiple of p"). Here, r = p / q, where q is constant and p is given by the input vector elements: 
VF_sinrpisine of p/q * p
VF_cosrpicosine of p/q * p
VF_sincosrpisine and cosine of p/q * p at once
VF_tanrpitangent of p/q * p
VF_cotrpicotangent of p/q * p
VF_secrpisecant of p/q * p
VF_cosecrpicosecant of p/q * p
 
Even more efficient versions use tables to obtain frequently-used values; these versions are denoted by the suffixes "rpi2" (multiples of p divided by an integer power of 2) and "rpi3" (multiples of p over an integer multiple of 3). Examples are: 
VF_sinrpi2 sine of p / 2n * p
VF_tanrpi3 tangent of p / (3*n) * p
 
Two more special trigonometric functions are: 
VF_sinc sinc function, Yi = sin( Xi ) / Xi
VF_Kepler Kepler function, calculating the time-dependent angular position of a planet or comet
 
Vectorized inverse trigonometric functions are available as 
VF_asinarc sin
VF_acosarc cos
VF_atanarc tan
VF_atan2 arc tan of ratios, Zi = atan( Yi / Xi )

Back to Table of Contents 

4.7 Analysis

There is a number of functions probing the analytical properties of data arrays:
 
VF_derivV derivative of a Y-array with respect to an X-array
VF_derivC the same for constant intervals between the X-values
VF_integralV value of the integral of a Y-array over an X-array
VF_runintegralV point-by-point ("running") integral
VF_integralC integral over an equally spaced X-axis
VF_runintegralC point-by-point integral over an equally spaced X-axis
VF_ismonoton test if an array is monotonously rising or falling
VF_iselementC test, if a given value occurs within a vector
VF_searchC search an ordered table for the entry whose value comes closest to a preset value C
VF_localmaxima detect local maxima (points whose right and left neighbours are smaller)
VF_localminima detect local minima (points whose right and left neighbours are larger)
VF_max detect global maximum
VF_min detect global minimum
VF_maxind global maximum and its index
VF_minind global minimum and its index
VF_absmax global maximum absolute value
VF_absmin global minimum absolute value
VF_absmaxind global maximum absolute value and its index
VF_absminind global minimum absolute value and its index
VF_maxexp global maximum exponent
VF_minexp global minimum exponent
VF_runmax "running" maximum
VF_runmin "running" minimum

The complex equivalents of the last group of functions are:
 
VCF_maxReIm maximum real and imaginary parts separately
VCF_minReIm minimum real and imaginary parts separately
VCF_absmaxReIm maximum absolute real and imaginary values separately
VCF_absminReIm minimum absolute real and imaginary values separately
VCF_absmax largest magnitude (absolute value; this is a real number)
VCF_absmin smallest magnitude
VCF_cabsmax complex number of largest magnitude
VCF_cabsmin complex number of smallest magnitude
VCF_sabsmax complex number for which the sum |Re| + |Im| is largest
VCF_sabsmin smallest complex number in terms of the sum |Re| + |Im|
VCF_absmaxind largest magnitude (absolute value) and its index
VCF_absminind smallest magnitude and its index
 
Sums, products, etc. are available by functions grouped as statistical building blocks and summarized in chapter 4.9

Back to Table of Contents 

4.8 Signal Processing:
Fourier Transforms and Related Topics

The following list of functions is available for signal processing applications:
 
VF_FFTtoC forward Fast Fourier Transform (FFT) of a real vector; the result is a cartesian complex vector
VF_FFT forward and backward FFT of a real vector; the result of the forward FFT is packed into a real vector of the same size as the input vector
VCF_FFT forward and backward FFT of a complex vector
VF_convolve convolution with a given response function
VF_deconvolve deconvolution, assuming a given response function
VF_filter spectral filtering
VF_spectrum spectral analysis
VF_autocorr autocorrelation function of a data array
VF_xcorr cross-correlation function of two arrays
VF_setRspEdit set editing threshold for the filter in convolutions and deconvolutions (decides over the treatment of "lost" frequencies)
VF_getRspEdit retrieve the current editing threshold

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 intermediate 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.
There are three different versions of all FFT-based functions. Depending on the memory model, either of them is automatically chosen. You may, however, explicitly specify the one you wish to employ.

As all FFT-based matrix functions internally rely on VF_FFT, all of them exist in three versions as well. Here, the prefixes are MFp_,  MFl_ and MFs_ in the real-number case, or MCFp_,  MCFl_ and MCFs_ in the complex case. Similarly to the one-dimensional case, the functions with the "normal" prefix (MF_,  MCF_) will automatically be redirected to the MFP_,  MFl_ or MFs_ variant, as determined by the memory model.

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.

Back to Table of Contents 

4.9 Statistical Functions and Building Blocks

The following collection of statistical functions is offered by OptiVec:
 
VF_sum sum of all elements
VI_fsum sum of all elements of an integer vector, accumulated as a floating point number in double or extended precision
VF_prod product of all elements
VF_ssq sum-of-squares of all elements
VF_sumabs sum of absolute values of all elements
VF_rms root-of-the-mean-square of all elements
VF_runsum running sum
VF_runprod running product
VF_sumdevC sum over the deviations from a preset constant, sum( |Xi-C| )
VF_sumdevV sum over the deviations from another vector, sum( |Xi-Yi| )
VF_avdevC average deviation from a preset constant, 1/N * sum( |Xi-C| )
VF_avdevV average deviation from another vector, 1 / N * sum( |Xi-Yi| )
VF_ssqdevC sum-of-squares of the deviations from a preset constant,
sum( (Xi - C)² )
VF_ssqdevV sum-of-squares of the deviations from another vector,
sum( (Xi - Yi)² )
VF_chi2 chi-square merit function
VF_chiabs "robust" merit function, similar to VF_chi2, but based on absolute instead of squared deviations
VF_mean equally-weighted mean (or average) of all elements
VF_meanwW "mean with weights" of all elements
VF_meanabs equally-weighted mean (or average) of the absolute values of all elements
VF_selected_mean averages only those vector elements which fall into a specified range, thus allowing to exclude outlier points from the calculation of the mean
VF_varianceC variance of a distribution with respect to a preset constant value
VF_varianceCwW the same with non-equal weighting
VF_varianceV variance of one distribution with respect to another
VF_varianceVwW the same with non-equal weighting
VF_meanvar mean and variance of a distribution simultaneously
VF_meanvarwW the same with non-equal weighting
VF_median median of a distribution
VF_corrcoeff linear correlation coefficient of two distributions
VF_distribution bins data into a discrete one-dimensional distribution function

Back to Table of Contents 

4.10 Data Fitting

Ranging from a simple linear regression to complex fitting problems involving multiple data sets and nonlinear functions with many adjustable parameters, OptiVec offers routines for virtually all practically occurring tasks of data fitting. As all of them, except for simple linear regression, rely on matrix methods, they actually form a part of MatrixLib. This means you have to #include <MFstd.h> (<MDstd.h>< MEstd.h>) or the unit MFstd, (MDstd, MEstd). In the 16-bit models of Borland C/C++, you also have to include the MC??.LIB file for the respective model.

A detailed description of the various data-fitting concepts is given in chapter 13 of MATRIX.HTM. Therefore, at this place, the available X-Y fitting functions are only summarized in the following table:
 
VF_linregress equally-weighted linear regression on X-Y data
VF_linregresswW the same with non-equal weighting
VF_polyfit fitting of one X-Y data set to a polynomial
VF_polyfitwW the same for non-equal data-point weighting
VF_linfit fitting of one X-Y data set to an arbitrary function linear in its parameters
VF_linfitwW the same for non-equal data-point weighting
VF_setLinfitNeglect set threshold to neglect (i.e. set equal to zero) a fitting parameter A[i], if its significance is smaller than the threshold
VF_getLinfitNeglect retrieve current significance threshold
VF_nonlinfit fitting of one X-Y data set to an arbitrary, possibly non-linear function
VF_nonlinfitwW the same for non-equal data-point weighting
VF_multiLinfit fitting of multiple X-Y data sets to one common linear function
VF_multiLinfitwW the same for non-equal data-point weighting
VF_multiNonlinfit fitting of multiple X-Y data sets to one common nonlinear function
VF_multiNonlinfitwW the same for non-equal data-point weighting
 

Back to Table of Contents 

4.11 Input and Output

VF_cprint print the elements of a vector 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. (Only for DOS)
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. (Only for DOS and EasyWin)
VF_fprint print a vector to a stream.
VF_write write data in ASCII format in a stream
VF_read read a vector from an ASCII file
VF_nwrite write n vectors of the same data type as the columns of a table into a stream
VF_nread read the columns of a table into n vectors of the same type
VF_store store data in binary format
VF_recall retrieve data in binary format

The following functions allow to modify the standard settings of VF_write,   VF_nwrite and VI_read
VF_setWriteFormat define a certain number format
VF_setWriteSeparate define a separation string between successive elements, written by VF_write
VF_setNWriteSeparate define a separation string between the columns written by VF_nwrite
V_setRadix define a radix different from the standard of 10 for the whole-number variants of the V.._read functions

Back to Table of Contents 

4.12 Graphics

VectorLib includes a range of data-plotting routines. Before any of them may be used, VectorLib graphics has to be initialized:
 
V_initPlot initialize VectorLib graphics functions (both Windows and DOS). For Windows, 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.
V_initGraph simultaneously initialize Borland's graphics interface and VectorLib plotting functions (DOS only). Do not call initgraph after V_initGraph. If, on the other hand, you have already called initgraph, do not use V_initGraph, but V_initPlot instead. At the end of the graphics session, the BGI function closegraph has to be used to leave the graphics mode and to release graphics buffer memory.
V_initPrint initialize VectorLib graphics functions and direct them to a printer (Windows only). By default, one whole page is reserved for plotting. In order to change this, call V_setPlotRegion after V_initPrint.
V_setPlotRegion set a plotting region different from the default

VectorLib distinguishes between two sorts of plotting functions, AutoPlot and DataPlot. All AutoPlot functions (e.g., VF_xyAutoPlot) execute the following steps:

  1. define a viewport within the plotting region (which is either the default region or the one defined by calling V_setPlotRegion)
  2. clear the viewport
  3. generate a Cartesian coordinate system with suitably scaled and labeled axes
  4. plot the data according to the parameters passed to the function
All DataPlot functions execute only the last step. They assume that a coordinate system already exists from a previous call to any of the AutoPlot functions, to V_findAxes, or to V_drawAxes. 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 SetViewportOrgEx (32-bit Windows), SetViewportOrg (16-bit Windows), or setviewport (DOS).

If you wish to switch back into text mode in DOS, use restorecrtmode. After that, a new call to V_initPlot (not V_initGraph!) brings you back into graphics mode.
For all plotting functions, the different plot styles (symbols, lines, and colors), are specified as parameters, see VF_xyAutoPlot. Here is a list of the available AutoPlot and DataPlot routines:
 
VF_xyAutoPlot display an automatically-scaled plot of an X-Y vector pair
VF_yAutoPlot plot a single Y-vector, using the index as X-axis
VF_xy2AutoPlot plot two X-Y pairs at once, scaling the axes in such a way that both vectors fit into the same coordinate system
VF_y2AutoPlot the same for two Y-vectors, plotted against their indices
VF_xyDataPlot plot one additional set of X-Y data
VF_yDataPlot plot one additional Y vector over its index

Cartesian complex arrays are printed into the complex plane (the imaginary parts versus the real parts), using
 
VCF_autoPlot plot one cartesian complex vector
VCF_2AutoPlot plot two cartesian complex vectors simultaneously
VCF_dataPlot plot one additional cartesian complex vector

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

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

Back to Table of Contents 


5. Error Handling

5.1 General Remarks

There are two general 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 OptiVec 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 no absolute overflow protection is possible for the extended-precision versions. They do not have a "safety margin" left as in the single- and double-precision 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. To be on the safe side, constant parameters should not exceed about 1.E32 for float, 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_; 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.

A series of identical errors occurring within one and the same OptiVec 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-2n 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.

Back to Table of Contents

5.2 Integer Errors

The only genuine integer errors are ZERODIVIDE errors (if a division by 0 is attempted). Other integer errors are neglected due to the implicit definition of integer operations as being performed modulo the respective power of 2 (see chapter 4.4). For those situations in which implicit modulo 2n arithmetics is not appropriate, OptiVec offers the possibility to trap these errors and print an error message and/or abort the program.
All functions where INTEGER OVERFLOW (e.g., in VI_rampVI_mulV, etc.) or INTEGER DOMAIN errors (e.g., in V_ItoU for negative X-values) may occur, exist in two versions: the "normal" version employs modulo 2n arithmetics and interchanges signed and unsigned data types according to their bit pattern. For the 16-bit and 32-bit integer types (but not for 8-bit and 64-bit), there is a second version which also employs modulo 2n arithmetics, but detects the errors. This second variant is denoted by the letter "o" (for "overflow detection") in the prefix: VIo_,  VSIo_,  VULo_, etc. (for the data-type interconversion functions, the prefix V_ is changed into Vo_). Here, the action to be taken in the case of INTEGER OVERFLOW or INTEGER DOMAIN errors has to be defined by calling V_setIntErrorHandling somewhere before the call to the VIo_ function. V_setIntErrorHandling takes an argument of the type V_ihand (defined in <VecLib.h> and the unit VecLib) with one of three possible values:
 
ierrNoteprint an error message
ierrAbortprint an error message and exit the program
ierrIgnore ignore the problem. With this last option, the error handling can be switched off intermediately.

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

C/C++ only:
To choose the overflow-detecting version not only for single function calls, but everywhere, the easiest way is to define symbolic constant V_trapIntError in the program header before(!) <VecLib.h> is included:
Example:
#define V_trapIntError 1
#include <VIstd.h>
#include <VImath.h>
.....
main() /* or WinMain(), or OwlMain() */
{
  iVector I1, I2;
  I1 = VI_vector( 1000 ); I2 = VI_vector( 1000 );
  V_setIntErrorHandling( ierrNote );
  VI_ramp( I1, 1000, 0, 50 ); /* an overflow will occur here! */
  V_setIntErrorHandling( ierrIgnore );
  VI_mulC( I2, I1, 1000, 5 );
    /* here, even a whole series of overflows will occur; they are all ignored. */
  ....
}

Back to Table of Contents

5.3 Floating-Point Errors

5.3.1 C/C++ specific

In order to understand the details of the floating-point error handling outlined in the following sections, you may wish to refer to the description of the functions _matherr and signal in the documentation of your C++ compiler.
(Borland C++ only: prior to the version 4.0, instead of _matherr() the function matherr() – without the leading underbar – was used, see below).
Keep in mind that _matherr and _matherrl are the user-definable focal points for the handling of all software-detected errors, whereas signal is used to install a handler for hardware-detected errors (which should better be avoided in the first place). Within the VectorLib functions, _matherr is used for the error handling in the VF_,  VCF_,  VD_, and VCD_ versions. _matherrl is used in the VE_ and VCE_ versions (Borland C++ only, as Visual C++ does not support 80-bit real numbers).

If the function in which an error occurs has one real-valued argument, only the parameter e->x is defined in calling _matherr and e->y is left undefined. Only if there are two arguments (like in VF_atan2 or in VF_cotrpi), both e->x and e->y are needed to hold these arguments. For complex arguments, the real part (or the Mag part for polar coordinates) is stored in e->x and the imaginary part (or the Arg part) in e->y.

Back to Table of Contents

5.3.2 Pascal/Delphi specific

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

Back to Table of Contents

5.3.3 Error Types (Both C/C++ and Pascal/Delphi)

For each VectorLib function, the types of errors that are detected and handled are described in the Alphabetical Reference. All functions derived from ANSI C or Pascal math functions (those whose declarations are to be found in <V?math.h> or the units V?math) contain a fully-fledged mathematical error handling. In addition to the error handling "by element", their return value shows if all elements have been processed error-free (return value FALSE or 0) or if an error occurred and was handled (return value TRUE or different from 0).

In the following description of all floating-point error types, 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.

Back to Table of Contents

5.3.4 Borland C++ only:
Differences between Borland C++ 4.0 and earlier versions

Borland C++ uses the function _matherr in the way described above only from version 4.0 on. Earlier versions employ the function matherr (without the leading underbar in the function name). In order to be usable both with the new and the old versions, VectorLib primarily calls matherr as for the older versions. The include-file <VecLib.h> provides a macro NEWMATHERR for the redirection of these calls to the new _matherr(). This macro should appear somewhere in the module containing the main() or WinMain() procedure, after the header:
#include <VecLib.h>
#include ...
NEWMATHERR
......
main()
{ ... }

Back to Table of Contents

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" float, 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. (This treatment is quite different from that chosen for the math functions of most compilers, where denormal arguments lead to SING errors also in these cases, which seems less appropriate to us.)

Back to Table of Contents

5.5 Advanced Error Handling: Writing Messages into a File

Quite generally, the libraries shipped with compilers do not offer the programmer much control over the way error messages are printed. 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. An additional motivation could come from the fact that, for any error occurring in a Windows program, a message box is displayed and program execution interrupted until you acknowledge having taken notice of the error.

You might wish to circumvent this. To this end, OptiVec 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.
Note that this redirection of error messages is valid only for errors occurring in OptiVec routines. If you wish to do so, however, there is a way in C/C++ to extend the redirection also to the "non-OptiVec" functions: you may modify _matherr and _matherrl such that the statement
return 0;
(which signals an unresolved error) is replaced by the sequence
V_noteError( e->name, e->type ); return 1;
Thereby the task of printing the error message for unresolved errors is passed to the OptiVec function V_noteError. Keep in mind that it is the return value of _matherr which decides if an error message is printed by the default error handler of your compiler. Thus, after the call to V_noteError, the printing of the default error messages is by-passed by returning "1". (Also, do not forget that OptiVec uses your _matherr routine to determine which errors you accept and which not!)

For example, your _matherr function (matherr – without the leading underbar – for Borland C++ 3.0 and 3.1) might look like the following one:
#include <math.h>
int _matherr( struct exception *e) /* "_exception" for MSVC */
{
  if( (e->type == UNDERFLOW) || (e->type == TLOSS) ) ; /* ignore */
  else /* all other errors deserve at least notice */
  {
    V_noteError( e->name, e->type );
    if (e->type == DOMAIN) exit(1); /* really fatal */
  }
  return 1;
}

(Of course, if you decide to change _matherr, do not forget to change _matherrl in the same way, if you are using Borland C++!).

Both C/C++ and Pascal/Delphi: The default printing of error messages on the screen alone is restored by V_closeErrorEventFile.

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 TRUE (non-zero) return value. You may wish to check for a clean result after a group of functions, like in the following example:
unsigned ErrFlag;
...
/* part Trig1 */
ErrFlag=0; /* reset the flag */
ErrFlag |= VF_sin( Y1, X1, sz );
ErrFlag |= VF_cos( Y2, X1, sz );
ErrFlag |= VF_atan2( Z1, Y1, Y2, sz );
if( ErrFlag ) V_printErrorMsg( "Errors occurred in part Trig1 ! ");
...

As indicated in the example, it is better to use the |= operator (Pascal/Delphi: "ErrFlag := ErrFlag or") instead of += (since, in rare cases, all return values might add up to 65536, which is stored as 0 due to an overflow of the integer variable). Even if you chose addition of the individual return values, the number of occurred errors would not be obtainable from the result; in case of an error, any non-specified non-zero number is returned.

Back to Table of Contents

5.6 OptiVec Error Messages

Just as with any C/C++ or Pascal program, errors occurring within mathematical functions lead to the appropriate error messages. See paragraph 5.3.3 for details.
Apart from math errors, there are some run-time errors specific to OptiVec routines. These errors lead to the messages noted below. The name of the function where the error occurred is not always exactly the name you wrote in your program. Instead of the prefix VI_, the message will read VLI_ or VSI_, depending on the memory model used. Similarly, instead of VU_, you will find VUL_ or VUS_.
VFs_FFT or VFl_FFT will be substituted for VF_FFT, again depending on the memory model. The reason for this behavior is that many functions share code and even names.

 

(00) OptiVec/CMATH not installed correctly
(must use INSTALL.EXE !)
Shareware version only: Either you are running a program containing OptiVec functions on a different computer than the one on which you installed OptiVec. The distribution of applications containing OptiVec functions is possible only with the Registered version.
Or you attempted to install OptiVec by unzipping the package by hand, without using INSTALL.EXE. This is not possible, as INSTALL.EXE also starts the clock for the trial period. With Windows 2000 / XP, you have to run InitOVBC.EXE (Borland C++ version) or InitOVVC.EXE (MSVC version) instead of INSTALL.EXE.

 

(0) Trial period for OptiVec/CMATH has expired!
Shareware version only: Consider ordering the registered version!

 

(1) Not enough memory.
You are trying to allocate memory, but there is not enough left. This error occurs mostly in connection with "forgotten" vectors that are allocated but never freed. Try these solutions:
* Look out for vectors that might be no longer needed and free them as soon as possible. Be sure that any vectors allocated in subroutines are freed before leaving the subroutine.
* Are you still working with 16-bit models? If you need to work with large amounts of data, the memory model FLAT should be used, working with Win32, WindowsNT, or Windows95/98.
* Store data intermediately on disk, using VF_store, and retrieve them using VF_recall, when needed. This method is slow and should be used only if really unavoidable.

 

(2) Vector > 64 kB not possible (16-bit).
(2) Vector > 4 GB not possible (32-bit).
* Either you are trying to allocate a vector whose size exceeds the maximum of 4 GB (32-bit) or 64 kB (16-bit except HUGE).
* Or you are in the HUGE model and attempt to process a huge vector in a function where the size is limited to 64 kB even in this model. This might happen, e.g., if the table is too large in one of the interpolation routines. In this case, you must either use the model FLAT or split up your problem into smaller vectors.

 

(3) Vectors/matrices must not be identical.
In some functions where more than one input vector (or matrix) is used to calculate more than one output vector (or matrix), attention has to be paid, which output data may or may not overwrite which input data. See the specification of the function where the error occurred.

 

(4) Cannot use requested format (too many entries per line).
This error occurs with the printing functions. The parameter nperline was chosen too large. The function automatically selects the maximum nperline possible and continues execution, but you should nevertheless consider adapting your program.

 

(5) X-values of table must be distinct.
The routines performing interpolations need the tables to be ordered according to their x-values. Each x-value may occur only once, as it is impossible for one and the same x-value to belong to different y-values.

 

(6) Not possible with fewer than n elements.
Some functions require a minimum size of n elements of the vector processed.

 

(7) Not possible with more than n elements.
Some functions are limited to a maximum size of n elements. This is true, for example, for VF_polyinterpol, where only up to 10 table elements may be used for the each interpolation.

 

(8) Size must be an integer power of 2.
For all functions using – explicitly or implicitly – Fast Fourier Transform methods, the vector size has to be an integer power of 2. Enlarge or truncate your vector(s) to meet that condition.

 

(9) Invalid parameter(s).
In some functions, certain combinations of input parameters are illegal. For example, it is not possible to perform a 9-point interpolation on only 5 data points.

 

(10) Cannot scale symbols by more than a factor of 50.
(Windows only.) The symbols used in VectorLib plotting functions cannot be magnified by more than a factor of 50 (which means already filling the screen with a single symbol). Program execution is continued with a value of 50.0.

 

(11) Cannot use line thicker than 500 pixels.
(Windows only.) The lines used in VectorLib plotting functions cannot be thicker than 500 pixels (which is already nonsense). Program execution is continued with a value of 500.

 

(12) Cannot free non-existent vector.
You called V_free or V_nfree for a vector that has no memory allocated. Program execution is continued without freeing anything.

Back to Table of Contents

 

(21) Invalid dimension(s).
In one way or the other, the dimensions of input or output matrices do not meet the requirements.

 

(22) Matrix is singular.
In a function requiring regular input matrices, a singular matrix was encountered. Consider alternative functions, designed to handle (nearly) singular matrices.

 

(23) No convergence achieved.
Some iterative matrix methods may, in very rare cases, fail to achieve convergence.

 

(24) Only the first n elements of each row can be printed.
This error occurs with the matrix printing functions. The number of columns is too large for the available linewidth. The function automatically selects the maximum number of columns to be displayed and truncates the rows as needed.


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. Although OptiVec has been tested thoroughly, there is, of course, always the possibility that a problem might have escaped our attention. Should you feel you discovered a "bug" in OptiVec, please try to specify the situation causing the problem as exactly as possible and let us know at support@optivec.com!

Back to Table of Contents

6.2 Problems with Windows3.x?

Programming for 16-bit Windows is much more involved than programming for either DOS or 32-bit Windows. 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 OptiVec. However, since they seem not to be very widely known, here is a collection of some of them. Up to now, these problems have not been observed with the memory model FLAT used with Win32 (Microsoft's 32-bit extension of Windows 3.1), Windows NT or Windows95/98/2000.

Back to Table of Contents

6.3 Problems with 32-bit Windows?

Until now, no problems of the type described above for 16-bit Windows have occurred. There has, however, been one nice surprise with the switch from Windows95 to Windows98: The Windows API function MessageBox now empties the number stack of the processor's FP unit – apparently for no justifiable reason. To be honest, it has taken us until version 3 of OptiVec to identify this Windows feature as the source of some spurious program crashes after math errors. (The constants A, B, C were suddenly missing in functions like VFx_exp after an overflow error.) Now, care is taken to save the number stack before all calls to MessageBox and restore it afterwards.

Back to Table of Contents

6.4 Problems with Borland's 16-bit Linker?

When working with large programs and libraries, older versions of TLINK sometimes run into problems. You may get error messages like "Linker stack overflow", "Out of memory", "Table limit exceeded", "Extended dictionaries ignored", or "Unresolved external xxx referenced from module yyy".
Try to give the linker as much memory as possible by closing applications, removing drivers etc. If that does not help, re-arrange your project list. Curiously enough, that solves the problem sometimes.
In the case of "Unresolved external" linker errors, there is only one way (if the error is not caused by wrong spelling). First you have to use TLIB in order to get a listing of the respective library (see the description of TLIB !). Screening the .LST file thus obtained with a text editor, you find the module containing the symbol which the linker was unable to locate. Using again TLIB, you have to extract this module from the library and add the resulting .OBJ file to your project list.

If you are still working with Borland (Turbo) C++ 3.x, you cannot simultaneously include VC??.LIB and MC??.LIB, as the linker is not able to handle these large libraries. As very few people are still using BC 3, we have discontinued full support for that compiler and do no longer offer the special BC 3 version OptiVec, previously fixing that problem by splitting the libraries up into many smaller ones. You still can extract the needed modules from the MC??.LIB libraries yourself, however, and include them as .OBJ nodes into your project.

Back to Table of Contents


7. The Include-Files and Units of OptiVec

All OptiVec C/C++ include-files have the same names as the corresponding Pascal/Delphi units. Naturally, the extension is different: ".H" for the C/C++ include-files, ".DCU" for Delphi units, and ".TPU" or "TPP" for Pascal units. Below, only the names are listed, without the extension.

C/C++ only: Declare the use of OptiVec functions with #include statements. If you are using MFC (Microsoft Foundation Classes) or Borland's OWL (ObjectWindows Library), the MFC or OWL include-files have to be #included before (!) the OptiVec include-files.

Pascal/Delphi only: Declare the use of OptiVec functions with the uses clause.
 
Include-file (add suffix .H) or unitContents
VecLibBasic definitions of the data types along with the functions common to all data types (prefix V_) except for the graphics initialization functions.
VFstd, VDstd, VEstdFloating-point "standard operations:" generation and initialization of vectors, index-oriented manipulations, data-type interconversions, statistics, analysis, geometrical vector arithmetics, Fourier-Transform related functions, I/O operations.
VCFstd, VCDstd, VCEstd,
VPFstd, VPDstd, VPEstd
Standard operations for cartesian and polar complex vectors
VIstd, VBIstd, VSIstd, VLIstd, VQIstdStandard operations for signed integer vectors
VUstd, VUBstd, VUSstd, VULstd, VUIstdStandard operations for unsigned integer vectors (VUIstd only for C/C++)
VFmath, VDmath, VEmathAlgebraic, arithmetical and mathematical functions for floating-point vectors
VCFmath, VCDmath, VCEmath,
VPFmath, VPDmath, VPEmath
Arithmetical and mathematical functions for complex vectors
VImath, VBImath, VSImath, VLImath, VQImathArithmetical and mathematical functions for signed integer vectors
VUmath, VUBmath, VUSmath, VULmath, VUImathArithmetical and mathematical functions for unsigned integer vectors (VUImath only for C/C++)
VgraphGraphics functions for all data types
VFNLFIT, VDNLFIT, VENLFITNon-linear fitting functions (Pascal/Delphi only; in C/C++, they are in M?std)
VFMNLFIT, VDMNLFIT, VEMNLFITNon-linear fitting functions for multiple data sets (Pascal/Delphi only; in C/C++, they are in M?std)
MFstd, MDstd, MEstdMatrix operations for real-valued matrices
MCFstd, MCDstd, MCEstdMatrix operations for cartesian complex matrices
MgraphMatrix graphics functions for all data types
MFNLFIT, MDNLFIT, MENLFITNon-linear fitting functions for Z = f(X, Y) data (Pascal/Delphi only; in C/C++, they are in M?std)
MFMNLFIT, MDMNLFIT, MEMNLFITNon-linear fitting functions for multiple Z = f(X, Y) data sets (Pascal/Delphi only; in C/C++, they are in M?std)
NEWCPLXcomplex class library CMATH; C++ only
CMATHcomplex library CMATH for Pascal/Delphi and plain C
CFMATH, CDMATH, CEMATHC/C++ only: type-specific parts of CMATH.
XMATHA few non-vectorized math functions needed internally by other OptiVec functions; they are publically accessible (see chapter 9). C/C++: declares also the sine, cosec, and tangent tables for VF_sinrpi2 etc.
FSINTAB2, DSINTAB2, ESINTAB3,
FSINTAB3, DSINTAB3, ESINTAB3
sine tables (Pascal/Delphi only; for C/C++, they are in XMATH)
FCSCTAB2, DCSCTAB2, ECSCTAB3,
FCSCTAB3, DCSCTAB3, ECSCTAB3
cosecant tables (Pascal/Delphi only; for C/C++, they are in XMATH)
FTANTAB2, DTANTAB2, ETANTAB3,
FTANTAB3, DTANTAB3, ETANTAB3
tangent tables (Pascal/Delphi only; for C/C++, they are in XMATH)
VecObjbasic definitions for VecObj, the object-oriented interface for C++
fVecObj, dVecObj, eVecObjVecObj member functions for real-valued vector objects (C++ only)
cfVecObj, cdVecObj, ceVecObj
pfVecObj, pdVecObj, peVecObj
VecObj member functions for complex vector objects (C++ only)
iVecObj, biVecObj, siVecObj, liVecObj, qiVecObjVecObj member functions for signed-integer vector objects (C++ only)
uVecObj, ubVecObj, usVecObj, ulVecObj, uiVecObjVecObj member functions for unsigned-integer vector objects (C++ only)
OptiVecincludes the whole OptiVec package (C++ only)
VecAllincludes all VectorLib and CMATH functions (C or C++ only)
MatAllincludes all MatrixLib functions (C or C++ only)
 

Back to Table of Contents


E N D

Copyright for OptiVec software and documentation
© 1996-2004 OptiCode – Dr. Martin Sander Software Dev.
All rights reserved!