./ 40700 1370 74 0 6641537437 7240 5ustar olegdiv60Makefile100600 1370 74 6661 6641015106 10633 0ustar olegdiv60# Makefile
# to build and test the Numerical Math Library (libla.a)
#
# To build the library, you may want to edit the list of library modules
# below (MODULES). Say, if you don't need/want FFT, remove all the names
# that contain fft from MODULES=. Then point ./c++ to the right
# script: g++.2.7.2 or g++.2.8.1 (see below if using a non-GNU C++ compiler).
# Then make the modules you chose:
# make lib
# A simple 'make' would suffice, too.
#
# To verify the library, do
# make check-all
# or, specifically,
# make vmatrix (checks elementary matrix operations)
# make vmatrix1 (checks less trivial matrix operations)
# make vmatrix2 (checks advanced matrix operations: multipl)
# make vlastreams (checks treams over matrices and vectors)
# make vvector (checks elementary vector operations)
#
# make vzeroin (verifies Brent's 1D root finder)
# make vfminbr (verifies Brent's 1D minimizer)
#
# make vali (check Aitken-Langrange interpolators)
# make vhjmin (check a multi-variate optimizer)
#
# make vsvd (check SVD decompositions)
# make vslesing (test solving generalized Ax=b)
#
# make vfft (check and clock FFT)
#
# make sample_ult
# make sample_adv
# Note, this Makefile was built (and works under) GNU make 3.71
#
# Please check RANLIB below and adjust it for your system if necessary
# (it was made for a BSD-like system)
# $Id: Makefile,v 4.3 1998/12/25 23:05:42 oleg Exp oleg $
# Make sure ./c++ points to the right g++ script.
# If using a non-GNU compiler, setting
# CC=cc -c and CCL=cc is a good starting point
CC=./c++
CCL=g++ -pipe
.SUFFIXES: .cc
MODULES=myenv.cc matrix1.cc matrix2.cc matrix_sub.cc \
vector.cc determinant.cc matrix_inv.cc \
zeroin.cc fminbr.cc ali.cc hjmin.cc svd.cc \
fft_init.cc fft_input.cc fft_output.cc
LIBRARY=libla.a
CLDEF= # Other flags for the linker, if needed
LIBS=-lm # Other libraries to use, if needed \
# When compiling FFT with SunWPro, add -lcomplex
#RANLIB = (ar d $(LIBRARY) __.SYMDEF || true); ranlib $(LIBRARY) # BSD-like
RANLIB=/bin/true # SYSV-based
# Rules, new style
%.o : %.cc
$(CC) $*.cc
$(LIBRARY)(%.o) : %.cc
$(CC) $*.cc
ar rcv $(LIBRARY) $*.o
rm -f $*.o
% : %.o $(LIBRARY)
$(CCL) $< $(CLDEF) $(LIBRARY) $(LIBS) -o $@
./$@
% :: %.cc
$(CC) $*.cc
$(CCL) $*.o $(CLDEF) $(LIBRARY) $(LIBS) -o $@
./$@
# Rules, old style
#.o: $*.o $(LIBRARY)
# $(CCL) $*.o $(CLDEF) $(LIBRARY) $(LIBS) -o $*
# ./$*
#.cc: $*.cc $(LIBRARY)
# $(CC) $*.cc
# $(CCL) $*.o $(CLDEF) $(LIBRARY) $(LIBS) -o $*
# ./$*
#.cc.o:
# $(CC) $*.cc
#
# Primary goal
# Library
lib: $(LIBRARY)
.PHONY: lib
.PRECIOUS: $(LIBRARY)
# Compile the source files that have been changed
$(LIBRARY): $(LIBRARY)($(MODULES:.cc=.o))
$(RANLIB)
#$(LIBRARY):: $(MODULES)
# $(CC) $?
# listobj=`echo $? | sed s/.cc/.o/g` ; \
# ar rv $(LIBRARY) $$listobj && \
# rm $$listobj
# $(RANLIB)
# Verification routines
check-all: vmatrix vvector vmatrix1 vmatrix2 vlastreams \
vali vhjmin vfminbr vzeroin \
vsvd vslesing vfft
clean:
rm -f *.o core vmatrix vvector vmatrix1 vmatrix2 vlastreams \
vali vhjmin \
vfminbr vzeroin sample_ult sample_adv \
vsvd vslesing vfft
dist-clean: clean
rm -f $(LIBRARY)
# Specific dependent goals
# Optimization causes g++'s internal compiler error...
#$(LIBRARY)(matrix1.o): matrix1.cc
# $(CC) -O0 matrix1.cc
# ar rcv $(LIBRARY) matrix1.o
# rm -f matrix1.o
#
#vmatrix.o : vmatrix.cc
# $(CC) -O0 vmatrix.cc
# Dependence rules
matrix1.cc: LinAlg.h
#$(LIBRARY):: LinAlg.h
# $(MAKE) -W matrix1.cc lib
NumMath.dr100600 1370 74 4451 6641512533 11074 0ustar olegdiv60 Numerical Mathematical Library
README Simple Introduction plus revision history
Makefile Makefile for making the library
Linear Algebra, Basic Operations
LinAlg.h Declaration of Matrices, Vectors, etc. over
real numbers
LAStreams.h Streams of REALs
matrix1.cc Level 1 BLAS, element-wise operations
matrix2.cc Level 2 BLAS, multiplications
matrix_sub.cc Level 1 & 2 BLAS, Matrix row/col/diag
matrix_inv.cc Find a matrix inverse
vector.cc Specific vector operations, Level 1 & 2 BLAS
determinant.cc Evaluate the determinant of a square matrix
vvector.cc Verify operations on vectors
vvector.dat Validation test output
vmatrix.cc Verify primitive operations on matrices
vmatrix1.cc Verify less trivial operations (norms,
forcing of promises, transpositions)
vmatrix2.cc Verify multiplication/inv operations on matrices
vlastreams.cc Verify streams over matrices and vectors
vmatrix.dat Validation test output
vmatrix1.dat Validation test output
vmatrix2.dat Validation test output
vlastreams.dat Validation test output
sample_adv.cc Sample code demonstrating a few "advanced" features
sample_adv.dat and comparisons with the traditional style
sample_ult.cc Sample code illustrating the use of LAStreams
sample_ult.dat (operations on triangular blocks of a matrix)
Numerical Math
math_num.h Declaration of the routines implementing various
numerical math algorithms
ali.cc Interpolation over the table of function values
vali.cc Verification program
vali.dat Validation test output
hjmin.cc Hooke-Jeeves multivariate optimization
vhjmin.cc Validation code
vhjmin.dat Validation test run
zeroin.cc Brent's root finder
fminbr.cc Brent's one-dimensional minimizer
vzeroin.cc Verification program
vfminbr.cc Verification program
vzeroin.dat Validation test output
vfminbr.dat Validation test output
svd.h Singular Value Decomposition
svd.cc Implementation of the SVD class
vsvd.cc Verification code
vslesing.cc Verification of the SVD applications
vsvd.dat Validation test output
vslesing.dat Validation test output
Fast Fourier Transform
fft.h Declaration of the class FFT
fft_init.cc Initialization routines
fft_input.cc Input sequence preprocessing
fft_output.cc Return the transform in the desired format
vfft.cc Verification routine
vfft.dat Test run output
builtin.h100600 1370 74 5705 6637304674 11030 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
/*
Copyright (C) 1988, 1992 Free Software Foundation
written by Doug Lea (dl@rocky.oswego.edu)
This file is part of the GNU C++ Library. This library is free
software; you can redistribute it and/or modify it under the terms of
the GNU Library General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version. This library is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
arithmetic, etc. functions on built in types
*/
#ifndef _builtin_h
#ifdef __GNUG__
#pragma interface
#endif
#define _builtin_h 1
#include
#include "std.h"
#include
#ifdef __GNUG__
#include
#endif
#ifdef __GNUG__
#define _VOLATILE_VOID volatile void
#else
#define _VOLATILE_VOID void
#endif
long gcd(long, long);
long lg(unsigned long);
// Otherwise, see myenv.h
#if defined(__GNUC__) && (__GNUC__ == 2) && (__GNUC_MINOR__ < 8)
extern "C" double start_timer();
extern "C" double return_elapsed_time(double last_time = 0.0);
#endif
#if defined(linux)
static inline div_t div(int numer, int denom)
{ const div_t res = { numer/denom, numer%denom}; return res;}
#endif
char* dtoa(double x, char cvt = 'g', int width = 0, int prec = 6);
unsigned int hashpjw(const char*);
unsigned int multiplicativehash(int);
unsigned int foldhash(double);
#if !defined(IV)
#ifndef __GNUG__ // has been defined in
inline double abs(double arg)
{
return (arg < 0.0)? -arg : arg;
}
inline float abs(float arg)
{
return (arg < 0.0)? -arg : arg;
}
#endif
inline short abs(short arg)
{
return (arg < 0)? -arg : arg;
}
#ifndef __GNUG__ // has been defined in
inline long abs(long arg)
{
return (arg < 0)? -arg : arg;
}
#endif
inline int sign(long arg)
{
return (arg == 0) ? 0 : ( (arg > 0) ? 1 : -1 );
}
inline int sign(double arg)
{
return (arg == 0.0) ? 0 : ( (arg > 0.0) ? 1 : -1 );
}
static inline long sqr(long arg)
{
return arg * arg;
}
#if ! _G_MATH_H_INLINES /* hpux and SCO define this in math.h */
static inline double sqr(double arg)
{
return arg * arg;
}
#endif
inline int even(long arg)
{
return !(arg & 1);
}
inline int odd(long arg)
{
return (arg & 1);
}
inline long lcm(long x, long y)
{
return x / gcd(x, y) * y;
}
inline void (setbit)(long& x, long b)
{
x |= (1 << b);
}
inline void clearbit(long& x, long b)
{
x &= ~(1 << b);
}
inline int testbit(long x, long b)
{
return ((x & (1 << b)) != 0);
}
#if !defined(M_PI)
# define M_PI 3.14159265358979323846
#endif
#endif
#endif
README100600 1370 74 130641 6641535122 10114 0ustar olegdiv60 Numerical Math Class Library
version 4.3
***** Version history is at the very end
***** Comments, questions, problem reports, etc.
are all very welcome. Please mail me at
oleg@pobox.com or oleg@acm.org or oleg@computer.org
***** Platforms
I have personally compiled and tested LinAlg 4.3 on
Linux 2.0.33 i586, gcc 2.7.2.1
HP 9000/770, HP-UX 10.10, gcc 2.8.1 (with optimization off)
UltraSparcII, Solaris 2.6, gcc 2.8.1 (with optimization off)
PowerMac 8500/132, BeOS Release 4, Metrowerk's CodeWarrior C++
Pentium, WinNT 4.0, Visual C++ 5.0
The previous (4.1) version also works on
SunSparc20/Solaris 2.4, gcc 2.7.2 and SunWorkshopPro
SGI, gcc 2.7.2 and SGI's native compiler
PowerMac 7100/80, 8500/132,
Metrowerk's CodeWarrior C++, v. 11-12
DEC Alpha (Digital UNIX Version 5.60), gcc 2.7.2
Note: if g++ version 2.8.1 is used to compile LinAlg, optimization
must be turned off. Otherwise, the compiler crashes with internal
compiler errors. Other compilers as well as gcc v2.7.2 don't seem to have
this problem.
***** Verification files: vmatrix vvector vmatrix1 vmatrix2 vlastreams
vali vhjmin vfminbr vzeroin
vsvd vslesing
Don't forget to compile and run them, see comments in the Makefile for
details. The verification code checks to see that all the functions
in this package have compiled and run well. The validation code can also
serve as an illustration of how package's classes and functions
may be employed.
***** Highlights and idioms
1. Data classes and view classes
Objects of a class Matrix are the only ones that _own_ data, that
is, 2D grids of REALs. All other classes provide different views of/into
the grids: in 2D, in 1D (a view of all matrix elements linearly arranged
in a column major order), a view of a single matrix row, column, or the
diagonal, a view of a rectangular subgrid, etc.
The views are designed to be as lightweight as possible: most of
the view functionality could be inlined. That is why views do not have any
virtual functions. View classes are supposed to be final, in Java parlance:
It makes little sense to inherit from them. Indeed, views are designed
to provide general and many special ways of accessing matrix elements, as
safely and efficiently as possible. Views turn out to be so flexible that
many former Matrix methods can now be implemented outside of the Matrix class.
These methods can do their job without a privileged access to Matrix'
internals while not sacrificing performance. Examples of such methods:
comparison of two matrices, multiplication of a matrix by a diagonal of
another matrix.
2. Matrix streams
Matrix streams provide a sequential view/access to a matrix or
its parts. Many of Linear Algebra algorithms actually require only sequential
access to a matrix or its rows/columns, which is simpler and faster than a
random access. That's why LinAlg stresses streams. There are two kinds of
sequential views: ElementWise streams are transient views that exist only
for the duration of an elementwise action. Hence these views don't require a
"locking" of a matrix. On the other hand, LAStream and the ilk are more
permanent views. An application traverses the stream at its own convenience,
bookmarking certain spots and possibly returning to them later. An LAStream
does assert a shared lock on the matrix, to prevent the grid of matrix
values from moving or disappearing.
Again, LAStreamIn etc. are meant to denote a stream under user's
control, which the user can create and play with for some time, getting
elements, moving the current position, etc. That is, LAStreamIn and the
Matrix object from which the stream was created (whose view the stream is)
can coexist. This arrangement is dangerous as one can potentially dispose
of the container before all of its views are closed. To guard against this,
a view asserts a "shared lock" on a matrix grid. Any operation that disposes
of the grid or potentially moves it in memory (like resize_to()) checks this
lock. Actually, it tries to assert an exclusive lock. Exclusive locking
of a matrix object will always fail if there are any outstanding shared
locks.
In contrast, ElementWiseConst and ElementWise are meant to be
transient, created only for the duration of a requested operation. That's
why it is syntactically impossible to dispose of the matrix that is
being viewed through a ElementWiseConst or ElementWise object. These
objects do not have any public constructors. This prevents a user from
building the view on his own (and forgetting to delete it). Therefore
creation of ElementWiseConst objects does not require asserting any locks,
which makes this class rather "light".
Matrix streams may stride a matrix by an arbitrary amount. This
allows traversing of a matrix along the diagonal, by columns, by rows, etc.
Streams can be constructed of a Matrix itself, or from other matrix views
(MatrixColumn, MatrixRow, MatrixDiag). In the latter case, the streams are
confined only to specific portions of the matrix.
Many methods and functions have been re-written to take advantage
of the streams. Notable examples:
// Vector norms are computed via the streams:
inline double Vector::norm_1(void) const
{ return of_every(*this).sum_abs(); }
inline double Vector::norm_2_sqr(void) const
{ return of_every(*this).sum_squares(); }
inline double Vector::norm_inf(void) const
{ return of_every(*this).max_abs(); }
The methods ElementWiseConst::sum_abs(), sum_squares(), and max_abs()
can also be applied to two collections. In this case, they work through
element-by-element differences between the collections.
An example of using LAStreams:
// Aitken-Lagrange interpolation (see ali.cc)
double ALInterp::interpolate()
{
LAStreamIn args(arg); // arg and val are vectors
LAStreamOut vals(val);
register double valp = vals.peek(); // The best approximation found so far
register double diffp = DBL_MAX; // abs(valp - prev. to valp)
// Compute the j-th row of the Aitken scheme and
// place it in the 'val' array
for(register int j=2; j<=val.q_upb(); j++)
{
register double argj = (args.get(), args.peek());
register REAL& valj = (vals.get(), vals.peek());
args.rewind(); vals.rewind(); // rewind the vector streams
for(register int i=1; i<=j-1; i++)
{
double argi = args.get();
valj = ( vals.get()*(q-argj) - valj*(q-argi) ) / (argi - argj);
}
....
}
return valp;
}
Note, all the streams are light-weight: they use no heap storage and leave no
garbage. All the operations in the snippets above are inlined.
A stride of a stride stream doesn't have to be an even multiple
of the number of elements in the corresponding collection, as the
following not-so-trivial example shows. It places a 'value' at the
_anti_-diagonal of a matrix m:
m.clear();
LAStrideStreamOut m_str(m,m.q_nrows()-1);
m_str.get(); // Skip the first element
for(register int i=0; ij)
inline void SVD::rotate(Matrix& U, const int i, const int j,
const double cos_ph, const double sin_ph)
{
MatrixColumn Ui(U,i), Uj(U,j);
for(register int l=1; l<=U.q_row_upb(); l++)
{
REAL& Uil = Ui(l); REAL& Ujl = Uj(l);
const REAL Ujl_was = Ujl;
Ujl = cos_ph*Ujl_was + sin_ph*Uil;
Uil = -sin_ph*Ujl_was + cos_ph*Uil;
}
}
The new version of the package, v4.3, rewrites this code in the
following way, avoiding random access to Matrix elements (and incident
range checks for MatrixColumns' indices).
inline void SVD::rotate(Matrix& U, const int i, const int j,
const double cos_ph, const double sin_ph)
{
LAStreamOut Ui(MatrixColumn (U,i));
LAStreamOut Uj(MatrixColumn (U,j));
while( !Ui.eof() )
{
REAL& Uil = Ui.get(); REAL& Ujl = Uj.get();
const REAL Ujl_was = Ujl;
Ujl = cos_ph*Ujl_was + sin_ph*Uil;
Uil = -sin_ph*Ujl_was + cos_ph*Uil;
}
}
LinAlg's implementation and validation code provides many more examples
of flexibility, clarity, and efficiency of streams.
4. Subranging a stream
LinAlg 4.3 implements subranging of a stream: creation of a new
stream that spans over a part of another. The latter can be either
a stride 1 or an arbitrary stride stream. The new stream may not _extend_
the old one: subranging is therefore a safe operation. A child stream
is always confined within its father's boundaries. A substream, once
created, lives independently of its parent: either of them can go out of
scope without affecting the other.
To create a substream, one has to specify its range as a
[lwb,upb] pair: an all-inclusive range. The 'lwb' may also be
given as -IRange::INF, and 'upb' as IRange::INF. These special values
are interpreted intelligently. Given an output stream, one may create
either an immutable, input substream, or allow modifications to a part
of the original stream. Obviously, given a read-only stream, only
read-only substreams may be created: the compiler will see to that at
_compile_ time. File sample_ult.cc provides a good example of using
substreams to efficiently reflect the upper triangle of a square matrix
onto the lower one (yielding a symmetric matrix). See vlastreams.cc and
SVD for more extensive examples of subranging.
As a good illustration to the power and efficiency of streams,
consider the following snippet from SVD::left_householder() (file svd.cc).
In LinAlg 3.2, the snippet was programmed as:
for(j=i+1; j<=N; j++) // Transform i+1:N columns of A
{
MatrixColumn Aj(A,j);
REAL factor = 0;
for(k=i; k<=M; k++)
factor += UPi(k) * Aj(k); // Compute UPi' * A[,j]
factor /= beta;
for(k=i; k<=M; k++)
Aj(k) -= UPi(k) * factor;
}
Version 4.3 uses substreams (which span only a part of matrix'
columns):
IRange range = IRange::from(i - A.q_col_lwb());
LAStreamOut UPi_str(MatrixColumn(A,i),range);
...
for(register int j=i+1; j<=N; j++) // Transform i+1:N columns of A
{
LAStreamOut Aj_str(MatrixColumn(A,j),range);
REAL factor = 0;
while( !UPi_str.eof() )
factor += UPi_str.get() * Aj_str.get(); // Compute UPi' * A[,j]
factor /= beta;
for(UPi_str.rewind(), Aj_str.rewind(); !UPi_str.eof(); )
Aj_str.get() -= UPi_str.get() * factor;
UPi_str.rewind();
}
5. Streams over an arbitrary rectangular block of a matrix
LinAlg 4.3 permits LAstreams that span an arbitrary rectangular
block of a matrix (including the whole matrix, a single matrix element,
a matrix row or a column, or a part thereof). You simply specify a matrix,
and the range of rows and columns to include in the stream.
vlastreams.cc verification code shows very many examples. The following
is a annotated snippet from vlastreams' output (vlastreams.dat)
---> Test LABlockStreams for 2:12x0:20
checking accessing of the whole matrix as a block stream...
checking modifying the whole matrix as a block stream...
# The first column of m
checking LABlockStreams clipped as [-INF,INF] x [-INF,0]
# The second column of m
checking LABlockStreams clipped as [2,INF] x [1,1]
# A part of the last column of m
checking LABlockStreams clipped as [3,12] x [20,INF]
# The first row of m
checking LABlockStreams clipped as [-INF,2] x [0,INF]
# The second row of m
checking LABlockStreams clipped as [3,3] x [-INF,20]
# A part of the last row of m
checking LABlockStreams clipped as [12,INF] x [-INF,19]
# The first matrix element
checking LABlockStreams clipped as [2,2] x [0,0]
# The last matrix element
checking LABlockStreams clipped as [12,INF] x [20,INF]
# A 2x3 block at the upper-right corner
checking LABlockStreams clipped as [3,4] x [18,INF]
# A 3x2 block at the lower-left corner
checking LABlockStreams clipped as [9,11] x [2,3]
checking modifying LABlockStreams clipped as [4,4] x [3,3]
With block streams, it is trivial to assign a block of one matrix
to a block of another matrix. See vlastreams.cc for examples.
6. Direct access to matrix elements
In LinAlg 3.2 and earlier, a Matrix contained a column index.
The index was used to speed up direct access to matrix elements
(like in m(i,j)). The index was allocated on heap and initialized upon
Matrix construction. In LinAlg 4+, a Matrix object no longer allocates
any index. The Matrix class therefore does not allow direct access to
matrix elements. This is done on purpose, to slim down matrices and make
them easier to build. Many linear algebra operations actually require
only sequential access to matrix elements. Thus the column index and other
direct access facilities are often redundant.
If one does need to access arbitrary elements of a matrix, the package
offers several choices. One may use a special MatrixDA view, which is
designed to provide an efficient direct access to a matrix grid. The MatrixDA
view allocates and builds a column index, and uses it to efficiently
compute a reference to the (i,j)-th element:
Matrix ethc = m;
MatrixDA eth(ethc);
for(register int i=eth.q_row_lwb(); i<=eth.q_row_upb(); i++)
for(register int j=eth.q_col_lwb(); j<=eth.q_col_upb(); j++)
eth(i,j) *= v(j);
see the validation code vmatrix*.cc, vvector.cc for more details. If creating
of an index seems like overkill, one may use MatrixRow, matrixColumn or
MatrixDiag. For example, the following statements are all equivalent:
MatrixRow(m,2)(3) = M_PI;
MatrixColumn(m,3)(2) = M_PI;
MatrixDA ma(m); ma(2,3) = M_PI;
(MatrixDA(m))(2,3) = M_PI;
Unlike Matrix itself, a Vector and all matrix views (MatrixRow,
MatrixColumn, MatrixDiag) allow direct access to their elements.
In most of the cases, MatrixDA behaves like a matrix. On occasions
when one needs to get hold of a real matrix, he can always
use MatrixDA::ref() or MatrixDA::get_container() methods:
Matrix mc(-1,10,1,20);
MatrixDA m(mc);
m.get_container().clear();
verify_element_value(m,0);
Again, it is strongly encouraged to use stream-lined matrix
operations as much as possible. As an example, finding of a matrix inverse
and computing of a determinant are implemented in this package using only
sequential matrix access. It is possible indeed to avoid direct access.
7. Const and Writable views
There are two flavors of every matrix view: a const and a writable
views, for example: ConstMatrixDA and MatrixDA, ConstMatrixColumn and
MatrixColumn, ElementWiseConst and ElementWise, LAStreamIn and LAStreamOut.
A const view can be constructed around a const matrix reference. A const
view provides methods that do not alter the matrix: methods that
query matrix elements, compare them, compute norms and other cumulative
quantities (sum of squares, max absolute value, etc). A writable view is
a subclass of the const view. That means that a writable view allows all
the query/comparison operations that the corresponding const view implements.
In addition, a writable view permits modification of matrix elements, via
assignment or other related operations (+=, *=, etc). Needless to say one
needs a non-const matrix reference to construct a writable view:
cout << " compare (m = pattern^2)/pattern with pattern\n";
m = pattern; to_every(m1) = pattern;
to_every(m).sqr();
assert( of_every(m).max_abs() == pattern*pattern );
to_every(m) /= of_every(m1);
verify_element_value(m,pattern);
to_every() makes a writable ElementWise view, while of_every() makes a
ElementWiseConst view.
8. Never return non-trivial objects (matrices or vectors)
Danger: For example, when the following snippet
Matrix foo(const int n)
{ Matrix foom(n,n); fill_in(foom); return foom; }
Matrix m = foo(5);
runs, it constructs matrix foo:foom, copies it onto stack as a return
value and destroys foo:foom. The return value (a matrix) from foo() is
then copied over to m via a copy constructor. After that, the return value
is destroyed. The matrix constructor is called 3 times and the
destructor 2 times. For big matrices, the cost of multiple
constructing/copying/destroying of objects may be very large. *Some*
optimized compilers can do a little better. That still leaves at least
two calls to the Matrix constructor. LazyMatrices (see below) can construct
a Matrix m "inplace", with only a _single_ call to the constructor.
9. Lazy matrices
Instead of returning an object return a "recipe" how
to make it. The full matrix would be rolled out only when and where
it is needed:
Matrix haar = haar_matrix(5);
haar_matrix() is a *class*, not a simple function. However similar
this looks to returning of an object (see the note above), it is
dramatically different. haar_matrix() constructs a LazyMatrix, an
object just of a few bytes long. A special "Matrix(const LazyMatrix&
recipe)" constructor follows the recipe and makes the matrix haar()
right in place. No matrix element is moved whatsoever!
Another example of matrix promises is
REAL array[] = {0,-4,0, 1,0,0, 0,0,9 };
test_svd_expansion(MakeMatrix(3,3,array,sizeof(array)/sizeof(array[0])));
Here, MakeMatrix is a LazyMatrix that promises to deliver a matrix
filled in from a specified array. Function test_svd_expansion(const Matrix&)
forces the promise: the compiler makes a temporary matrix, fills
it in using LazyMatrix's recipe, and passes it out to test_svd_expansion().
Once the function returns, the temporary matrix is disposed of.
All this goes behind the scenes. See vsvd.cc for more details (this is
where the fragment was snipped from).
Lazy matrices turned out so general and convenient that they subsumed
glorified constructors (existed in the previous version of the LinAlg). It is
possible to write now:
Matrix A = zero(B);
Matrix C = unit(B);
Matrix D = transposed(B);
A = unit(B);
D = inverse(B);
In all these cases, the result is computed right in-place, no temporary
matrices are created or copied.
Here's a more advanced example,
Matrix haar = haar_matrix(5);
Matrix unitm = unit(haar);
Matrix haar_t = transposed(haar);
Matrix hht = haar * haar_t; // NB! And it's efficient!
Matrix hht1 = haar; hht1 *= haar_t;
Matrix hht2 = zero(haar); hht2.mult(haar,haar_t);
verify_matrix_identity(unitm,hht);
verify_matrix_identity(unitm,hht1);
verify_matrix_identity(unitm,hht2);
With lazy matrices, it is possible to express a matrix multiplication
in a "natural way", without the usual penalties:
C = A * B;
Here again, the star operator does not actually multiply the matrices. It
merely constructs a lazy matrix, a promise to compute the product (when the
destination of the product becomes known). The promise is then forced by the
assignment to matrix C. A check is made that the dimensions of the promise
are compatible with those of the target. The result will be computed right
into matrix's C grid, no temporary storage is ever allocated/used.
As even more interesting example, consider the following snippet
from the verification code:
verify_matrix_identity(m * morig,unit(m));
Here both argument expressions compute matrix promises; the promises are
forced by parameter passing, so that the function can compare the resulting
matrices and verify that they are identical. The forced matrices are
discarded afterwards (as we don't need them). Note how this statement leaves
no garbage.
A file sample_adv.cc tries to prove that LazyMatrices do indeed
improve performance. You can see this for yourself by compiling and
running this code on your platform. LazyMatrices are typically 2 to
three (Metrowerks C++, BeOS) times as efficient.
10. Lazy matrices and expression templates
Lazy matrices and expression templates are a bit different.
Expression templates are a _parser_ of expressions, an embedded language.
They parse an expression like
C = A*B;
at compile time and generate the most efficient, inlined code. This
however places an immense burden on a compiler. For one thing, the
compiler has to optimize out _very_ many temporary objects of various types,
which emulate Expression templates parser's stack. The compiler should
be able to automatically instantiate templates; gcc until recently couldn't,
and even now I won't dare to ask it to. Furthermore, one has to be very
careful writing expression templates: every type of expression
(multiplication, addition, parenthesized expression, left-right
multiplication by a scalar (a double, float,...)), etc. requires
its own template. One has to make sure that multiplication
is indeed performed ahead of addition, according to usual operation
priorities, as in D = A + B*C; Templates are not the best tool to write
parsers to start with. The very need to write one's own parser in C++ (which
already has a parser) appears rather contrived: this may show that C++
might not be the right language for such problems after all.
Lazy matrices do not require any templates at all; multiplication
itself as in
C = A*B;
is performed by a separate function, which may be a library function
and may not even be written in C++: as a typical BLAS, matrix multiplication
can be coded in assembly to take the best advantage of certain architectures.
Lazy matrices is a technique of dealing with an operation like
A*B when its target is not yet known. In the standard C++ paradigm,
the expression is evaluated and the result is placed into a temporary.
Lazy matrices offer a way to delay the execution until the target,
the true and the final recipient of the product, becomes known.
One doesn't need any matrix temporary then. Lazy computation is the
standard fare of functional programming; in Haskell, all computation is
lazy. That is, a Haskell code isn't in a hurry to execute an operation,
it waits to see if the results are really necessary, and if so,
where to place them. It's interesting that a similar technique is
possible in C++ as well.
As lazy matrices and expression templates are rather independent,
one can mix and match them freely.
11. Accessing row/col/diagonal of a matrix without much fuss
(and without moving a lot of stuff around)
Matrix m(n,n); Vector v(n);
to_every(MatrixDiag(m)) += 4; // increments the diag elements of m
// by 4
v = of_every(ConstMatrixRow(m,1)); // Saves a matrix row into a vector
MatrixColumn(m,1)(2) = 3; // the same as m(2,1)=3, but does not
// require constructing of MatrixDA
(MatrixDiag(m))(3) = M_PI; // assigning pi to the 3d diag element
Note, constructing of, say, MatrixDiag does *not* involve any copying
of any elements of the source matrix. No heap storage is used either.
12. Nested functions
For example, creating of a Hilbert matrix can be done as follows:
void foo(const Matrix& m)
{
Matrix m1 = zero(m);
struct MakeHilbert : public ElementAction
{
void operation(REAL& element, const int i, const int j)
{ element = 1./(i+j-1); }
};
m1.apply(MakeHilbert());
}
A special method Matrix::hilbert_matrix() is still more optimal, but not
by the whole lot. The class MakeHilbert is declared *within* a function
and is local to that function. This means one can define another MakeHilbert
class (within another function or outside of any function - in the global
scope), and it will still be OK.
Another example is application of a simple function to each matrix element
void foo(Matrix& m, Matrix& m1)
{
class ApplyFunction : public ElementWiseAction
{
double (*func)(const double x);
void operator () (REAL& element) { element = func(element); }
public: ApplyFunction(double (*_func)(const double x)) : func(_func) {}
};
to_every(m).apply(ApplyFunction(sin));
to_every(m1).apply(ApplyFunction(cos));
}
Validation code vmatrix.cc and vvector.cc contains a few more examples
of this kind (especially vmatrix1.cc:test_special_creation())
13. SVD decomposition and its applications
Class SVD performs a Singular Value Decomposition of a rectangular matrix
A = U * Sig * V'. Here, matrices U and V are orthogonal; matrix Sig is a
diagonal matrix: its diagonal elements, which are all non-negative, are
singular values (numbers) of the original matrix A. In another interpretation,
the singular values are eigenvalues of matrix A'A.
Application of SVD: _regularized_ solution of a set of simultaneous
linear equations Ax=B. Matrix A does _not_ have to be a square matrix.
If A is a MxN rectangular matrix with M>N, the set Ax=b is obviously
overspecified. The solution x produced by SVD_inv_mult would then be
the least-norm solution, that is, a least-squares solution.
Note, B can be either a vector (Mx1-matrix), or a "thick" matrix. If B is
chosen to be a unit matrix, then x is A's inverse, or a pseudo-inverse if
A is non-square and/or singular.
An example of using SVD:
SVD svd(A);
cout << "condition number of matrix A " << svd.q_cond_number();
Vector x = SVD_inv_mult(svd,b); // Solution of Ax=b
Note that SVD_inv_mult is a LazyMatrix. That is, the actual computation
occurs not when the object SVD_inv_mult is constructed, but when it's
required (in an assignment).
14. Functor as a function pointer
Package's fminbr(), zeroin(), hjmin() are functions of "the higher
order": they take a function, any function, and try to find out a
particular property of it - a root or a minimum. In LinAlg 3.2 and
earlier, this function under investigation was specified as a pointer
to a C/C++ function. For example,
static int counter;
static double f1(const double x) // Test from the Forsythe book
{
counter++;
return (sqr(x)-2)*x - 5;
}
main()
{
counter = 0;
const double a = 0, const double b = 1.0;
const double found_location_of_min = fminbr(a,b,f1);
printf("\nIt took %d iterations\n",counter);
}
Note that function f1(x) under investigation had a side-effect: it
alters a 'counter', to count the total number of function's
invocations. Since f1(x) is called (by fminbr()) with only single
argument, the 'counter' has to be declared an external variable. This
makes it exposed to other functions of the package, which may
inadvertently change its value. Also, the main() function should know
that f1() uses this parameter 'counter', which main() must initialize
properly (because the function f1() obviously can't do that itself)
The new, 4.0 version of the package provides a more general
way to specify a function to operate upon. A math_num.h file declares
a generic function class, a functor, which takes a single
floating-point argument and returns a single FP (double) number as the
result:
struct UnivariateFunctor {
virtual double operator() (const double x) = 0;
};
The user should specialize this abstract base class, inheriting from
it and specifying "double operator()" to be a function being
investigated. The user may also add his own data members and methods
to his derived class, which would serve as an "environment" for the
function being optimized. The user should then instantiate this class
(thus initializing the environment), and pass the instance to
fminbr(). The environment will persist even after fminbr() returns,
so the user may use accumulated results it may contain. For example,
// Declaration of a particular functor class
class F1 : public UnivariateFunctor
{
protected:
int counter; // Counts calls to the function
const char * const title;
public:
ATestFunction(const char _title []) : counter(0), title(_title) {}
double operator() (const double x) { counter++; return (sqr(x)-2)*x - 5; }
int get_counter(void) const { return counter; }
};
main()
{
const double a = 0, const double b = 1.0;
F1 f1("from the Forsythe book"); // Instantiating the functor
const double found_location_of_min = fminbr(a,b,f1);
printf("\nIt took %d iterations\n",f1.get_counter());
}
Note how similar this looks to the previous example. The way fminbr()
is called hasn't changed at all! This is to be expected: from the
operational point of view, a function class is almost identical to a
function pointer. In a sense, a function class is a syntactic wrapper
around a function pointer, which is tucked away into a virtual table
of UnivariateFunctor class. However, there are some differences: For
one, main() does not care any more about global data f1() may use, and
how they should be initialized. It's the job of a functor's
constructor. Also, since the function pointer itself as well as
function's private environment ('counter', for example) are hidden,
there is no way one can mess them up, leave uninitialized, or use
inappropriately.
The validation code vfminbr.cc and vzeroin.cc shows further refinement
of this idea. Please mail me if you have any question or comment.
***** Grand plans
Computing of a random orthogonal matrix
Saving/loading of a matrix
Finding matrix Extrema (and extrema of abs(matrix))
Compute X'AX for a square matrix
Compute x'Ax (a square form)
Asymmetry index
Add an ArithmeticProgression class ("virtual" constant vector)
Recompile and beautify FFT and SVD parts, taking advantage of
streams as much as possible. For example, FFT should take and
transform a StrideStream rather than a vector. In that case, one can
apply FFT to the whole matrix, or a separate matrix row or
a column.
Make FFT itself a stream class (a Complex stream). This will
obviate the need for FFT:real(), FFT::abs(), FFT::abs_half() etc.
methods.
In FFT, add functions to compute 2D sin/cos transforms.
Make an FFT FAQ (questions I answered through e-mail,e.g., 2D FFT,
DFT vs. Fourier integral, etc). Describe in detail how to perform an
inverse transform.
Make a special class for SymmetricMatrix
When gcc starts supporting member function templates, make
iterator classes for iterating over a vector, two vectors, Matrix
slices, etc.
Code to verify a matrix (pseudo)inverse, that is,
test Moore-Penrose conditions XAX = X, AXA = A; AX and XA are
symmetric (that is, AX = (AX)', etc.) where X is a (pseudo)inverse
of A.
Another SVD application: compute a covariance matrix for a
given design matrix X, i.e. find the inverse of X'X for a
rectangular matrix X.
SVD optimization: when solving Ax=b via SVD, one doesn't need
to accumulate matrix U: one can apply the transformations directly
to b. That is, make SVD take matrices U,V as input (which must be
appropriately initialized), so SVD would accumulate transformations
in them. U,V don't have to be initialized as unit matrices at all
(and may be omitted).
Add ispline(): building a spline and integrating it
Add slehol: solve a set of SLE with a symmetric matrix
Lazy matrix operators A+B, multtranspose(A,B), etc.
Overload operator "," for inline filling in of vectors, as in
Vector v = VectorInline, 0, 1.0, 3;
which creates a vector of three elements and assigns initial
values to them.
Introduce matrix streams of the second order, that is, a
stream that traverses a matrix column-by-column, and can create
a column stream to access the current column. The same for the
rows.
Note an article "Scientific Computing: C++ vs Fortran", Nov 1997
issue of DDJ (described in my DDJ notes)
Add dump(), validate() methods to every class!
Note suggestions by Marco Tenuti (in particular, computing
the rank of a matrix)
Maybe I have to make a (protected) method
"bool Matrix::q_within(const REAL * const p) const"
and use it in LAStreamIn, etc. wherever I need to make sure the pointer
is within matrix's grid. Maybe this should be Matrix::ConstReference's
method.
Consider adding MAC (multiply-accumulate) "instruction" similar
to the one in DSP. Note that streaming is a typical memory
access model of DSPs: that's why DSP almost never incorporate a
a data cache (See "DSP Processors Hit the Mainstream,"
Jennifer Eyre and Jeff Bier, Computer, Vol. 31, No. 8, August 1998,
pp. 51-59). Also refer to an article "Smarter Memory: Improving
Bandwidth for Streamed References", Computer, July 1998, pp.54-63. A
memory architecture designed in the article achieves low
overall latencies because it was told by a compiler that
a stream operation is to follow. Thus streaming is a deep
software/hardware concept.
Add streams over Matrix triangles (although they can easily be
emulated with regular or block streams -- and appropriate 'seek'-ing).
Construct ElementWiseConst from LAStreamIn or LAStreamOut;
Make all ElementWise iterators (to_every(), of_every() constructors)
accept an IRange argument.
Note mentioning of LinAlg in
http://z.ca.sandia.gov/~if-forum/list/msg00000.html
Replace all 'double' with REAL_EXT
Think about combinators of a form of_every(of_every1,of_every2);
In particular, "ElementWiseAction& apply(ElementWiseAction1& functor,
ConstElementWise&);" where functor takes one argument and
returns one value. Generalize this 'apply' to take two
ConstStreams and a functor that takes two arguments. This is
similar to Scheme's map. Thus it should be possible to
write "to_every(V).apply(adder(x),of_every(V1));" to
compute V += V1*x;
How to calculate: M = N*H*3.5; without modifying the matrices N and H?
(Boris Holscher's question).
A member function to return the first element of the matrix, so
one can easily convert a 1x1 matrix to a scalar
(Boris Holscher's suggestion)
Note a similarity between streams and restricted pointers (a
proposed addition to ANSI C by a Numerical C Extensions Group).
Restricted pointers are a means of asserting an aliasing constraints
(if an array is not being modified via a restricted pointer, a
compiler may assume the array is not being modified at all).
***** Revision history
Version 4.3 (Christmas 1998)
Improved portability: LinAlg now compiles with gcc 2.8.1, Visual
C++ and Metrowerk's C++. I heard that SGI's native compiler
takes LinAlg 4.3 as well.
The package is complete now: SVD and FFT have been finally
ported from the 3.2 version.
Genuine Lambda-abstractions: see vzeroin.cc and vfminbr.cc
MinMax class (in LinALg.h) is extended with methods to permit
accumulation of min and max; see svd.cc for a usage example.
Introducing IRange and IRangeStride classes in LinALg.h
Added an explanation of differences between Lazy Matrices and
expression template styles. See this README file, above.
Introducing streams over an arbitrary rectangular block of a
matrix
All streams have now an ability to 'seek(offset, seek_dir)' in
a manner similar to that of ordinary iostreams.
In particular, one can 'ignore(how_many)' elements in a stream.
See the explanation above for more details.
One can _subrange_ a stream: create a new stream that spans over
a part of another. The new stream may not _extend_ the old one.
When specifying range boundaries, -INF and INF are permitted, and
interpreted intelligently. One may create an input stream as a
subrange of an output stream. See notes above for more details.
Changed ElementAction and ConstElementAction: they are now
pure interfaces; the index of the current element is passed
as an argument to the interface's 'operation'
Note, SVD::rotate is actually a general-purpose function.
builtin.h provides now its own implementation of div() on Linux.
The implementation of div() in linux's libc.a does not agree with
gcc's optimizations (thanks to Erik Kruus for pointing this out).
Added pow(double,long) and pow(long,long) to myenv.cc
They used to be in libg++; yet libstdc++ has dropped them: they
are not standard yet often convenient
LAStreams.h is separated from LinAlg.h to make LinAlg.h smaller
and more manageable
Trivial changes to the FFT package: taking advantage of
LAStreams on few occasions, making sure everything works and
verifies.
Version 4.1
Completely redesigned the entire hierarchy, based on container/view/
stream principles. See above for more details.
Matrix class per se no longer provides a direct access to matrix
elements. Use MatrixDA, MatrixRow, MatrixColumn, or MatrixDiag
views.
MatrixRow, MatrixCol, MatrixDiag, LazyMatrix all inherit from
DimSpec, that is, all answer queries about their dimensions
(row/col lower and upper bounds).
Added lazy matrix operator * (const Matrix&B, const Matrix&C), so
that it is possible to write now Matrix D = A * B;
operator *= (Matrix& m, const ConstMatrixDiag& diag)
is not a member of the Matrix class any more: it does not
need any privileged access to a matrix object (nor ConstMatrixDiag
class). The multiplication itself is implemented with streams.
(and makes a good example of their usage). It is almost just as
efficient as the priviledged implementation: all loops
are inlined, no extra procedures/functions are called
while traversing the matrices and multiplying their elements.
By the same token, computing of Vector norms no longer requires
any direct access to vector's elements. Entire computation is
a trivial application of an ElementWiseConst iterator.
No need for separate operations on MatrixColumn, etc:
it's enough that MatrixColumn can be tacitly converted to
ElementWise stream, meaning that the whole suite of elementwise
operations immediately applies.
Glorified Matrix constructors are gone. They are subsumed by Lazy
matrices (see the discussion above).
The matrix inversion algorithm is tinkered with to avoid a direct
access to matrix' elements (that is, avoid using a column matrix
index). That's why a method invert() can be applied to a
Matrix itself, rather than MatrixDA.
Made constructors (iterators?) for a Vector from MatrixRow/Col/Diag
ali.cc and hjmin.cc are re-written to take advantage of LAStreams:
most of the time they use only sequential access to vector
elements.
New style casts (const_cast, static_cast) are used throughout.
Use of mixin inheritance throughout (DimSpec, AREALStream, etc.)
Tests (validation code) remained mostly unchanged. The only
modifications were insertions of to_every()/of_every() in a few
places. This shows how much of the LinAlg interface remained the same.
ElementPrimAction is replaced with ElementWiseAction/
ElementWiseConstAction, which are to be applied to a
(resp, writable and const) ElementWise stream.
The code and comments in zeroin.cc and fminbr.cc are greatly
embellished: The code is now more in the C++ true spirit.
The function under investigation is now a functor
(a "clause") rather than a mere function pointer;
DBL_EPSILON is used throughout, instead of my own
EPSILON (which is deprecated)
Declarations in math_num.h are adjusted accordingly.
Validation code vzeroin.cc and vfminbr.cc is almost completely
re-written to take advantage and show off functors
(function "clauses") as arguments to zeroin() and fminbr().
Furthermore, test cases themselves are declared as anonymous,
"lambda"-like functions, similar to Scheme/Dylan even in
appearance.
The validation code is also expanded with new test cases,
from Matlab:Demos:zerodemo.m
use M_PI instead of PI (in the fft package)
FFT package embellished for the new gcc
determinant.cc is rewritten using only _serial_ access to Matrix
elements. The algorithm is faster and clearer now.
Makefile is beautified: c++l is gotten rid of
Corrected spellings for Householder and Hooke-Jeeves
corrected an insidious bug in SVD::rip_through() method of QR part
of svd.cc:
only abs values of quantities (quantity f) makes sense to compare
with eps; many thanks to W. Meier
vsvd.cc now includes the test case W. Meier used to show the bug
fabs() is replaced with abs() throughout the package (which does
not actually change functionality, but makes the code more generic)
Version 3.2
hjmin(), Hooke-Jeeves optimization, embellished and tested
ali.cc beautified, using nested functions, etc. (nice style)
Added SVD, singular value decomposition, and a some code
to use it (Solving Ax=b, where A doesn't have to be rectangular,
and b doesn't have to be a vector)
Minor embellishments
using bool datatype wherever appropriate
short replaced by 'int' as a datatype for indices, etc.: all
modern CPUs handle int more efficiently than short
(and memory isn't that of a problem any more)
Forcing promise (LazyMatrix) on assignment
Testing Matrix(Matrix::Inverted,m) glorified constructor
added retrieving an element from row/col/diag
added Matrix C.mult(A,B); // Product A*B
*= operation on Matrix slices
Making a vector from a LazyMatrix
made sure that if memory is allocated via new, it's disposed
of via delete; if it was allocated via malloc/calloc, it's
disposed of via free(). It's important for CodeWarrior, where
new and malloc() are completely different beasts.
Version 3.1
Deleted dummy destructors (they're better left inferred: it results
in more optimal code, especially in a class with virtual functions)
Minor tweaking and embellishments to please gcc 2.6.3
#included and where missing
Version 3.0 (beta): only Linear Algebra package was changed
got rid of a g++ extension when returning objects (in a_columns, etc)
removed "inline MatrixColumn Matrix::a_column(const int coln) const"
and likes: less problems with non-g++ compilers, better portability
Matrix(operation::Transpose) constructors and likes,
(plus like-another constructor) Zero:, Unit, constructors
invert() matrix
true copy constructor (*this=another; at the very end)
fill in the matrix (with env) by doing ElementAction
cleaned Makefile (pruned dead wood, don't growl creating libla
for the first time), a lots of comments as to how to make things
used M_PI instead of PI
#ifndef around GNU #pragma's
REAL is introduced via 'typedef' rather than via '#define': more
portable and flexible
added verify_element_value(), verify_matrix_identity() to the main
package (used to be local functions). They are useful in
writing the validation code
added inplace and regular matrix multiplication: computing A*B and A'*B
all matrix multiplication code is tested now
implemented A*x = y (inplace (w/resizing))
Matrix::allocate() made virtual
improved allocation of vectors (more optimal now)
added standard function to make an orthogonal Haar matrix
(useful for testing/validating purposes)
Resizing a vector keeps old info now (expansion adds zeros (but still
keeps old elements)
universal matrix creator from a special class: Lazy Matrices
Version 2.0, Mar 1994: Only LinAlg package was changed significantly
Linear Algebra library was made more portable and compiles now
under gcc 2.5.8 without a single warning.
Added comparisons between a matrix and a scalar (for-every/all -type
comparisons)
More matrix slice operators implemented
(operations on a col/row/diag of a matrix, assignments them
to/from a vector)
Other modules weren't changed (at least, significantly), but work
fine with the updated libla library
Version 1.1, Mar 1992 (initial revision)
fft.h100600 1370 74 13343 6640003036 10134 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Fast Fourier Transform
*
* This package computes sums of the form
* (1) xf[k] = SUM{ x[j] * exp(-2*PI*I/N j*k), j=0..N-1 }, k=0..N-1
* (2) x[j] = 1/N SUM{ xf[k] * exp(+2*PI*I/N j*k), k=0..N-1 }, j=0..N-1
* (3) xf[k] = SUM{ x[j] * exp(-2*PI*I/N j*k), j=0..N/2-1 }, k=0..N/2-1
*
* where N is an exact power of two.
*
* Formula (1) defines the classical Discrete Fourier transform, with x[j]
* being a real or complex sequence. The result xf[k] is always a complex
* sequence; the user however may choose to retrieve only its real or
* imaginary part, or any combination thereof (for example, the power spectrum).
* Formula (2) is the inverse formula for the DFT.
* Formula (3) is nothing but a trapezoid rule approximation to a
* Fourier integral; the trapezoid rule is the most stable one with respect
* to the noise in the input data. Again, x[j] may be either a real or
* a complex sequence. The mesh size other than 1 can be specified as well.
*
* $Id: fft.h,v 1.4 1998/12/22 20:50:38 oleg Exp oleg $
*
************************************************************************
*/
#if !defined(__GNUC__)
#pragma once
#else
#pragma interface
#endif
#ifndef _fft_h
#define _fft_h 1
#include "LAStreams.h"
#include
#if defined(__GNUC__)
#include
#if (__GNUC__ == 2) && (__GNUC_MINOR__ < 8)
#define std
#endif
#else
#include
#if defined(MSIPL_COMPLEX_H) && !defined(MSIPL_USING_NAMESPACE)
#define std
#else
using namespace std;
#endif
typedef std::complex double_complex;
#endif
typedef double_complex Complex;
class FFT
{
RWWatchDog ref_counter; // To control read/write access
// to this class
const int N; // No of points the FFT packet
// has been initialized for
int logN; // log2(N)
const double dr; // Mesh size in the r-space
Complex * A; // [0:N-1] work array
// the transform is placed to
Complex * A_end; // Ptr to the memory location next to
// the last A element
short * index_conversion_table; // index_conversion_table[i]
// is a bit-inverted i, i=0..N
Complex * W; // FFT weight factors
// exp( -I j 2pi/N ), j=0..N-1
// Private package procedures
void fill_in_index_conversion_table(void);
void fill_in_W(void);
void complete_transform(void);
// Those aren't implemented; but making them
// private forbids the assignement
FFT(const FFT&);
void operator= (const FFT&);
public:
// Constructor; n is the number of points to transform,
// dr is the grid mesh in the r-space
FFT(const int n, const double dr=1);
~FFT(void);
// Fundamental procedures,
// Input the data and perform the transform
void input( // Preprocess the real input sequence
const Vector& x); // Real [0:N-1] vector
void input( // Preprocess the complex input seq
const Vector& x_re, // [0:N-1] vector - Re part of input
const Vector& x_im); // [0:N-1] vector - Im part of input
// Preprocess the input with zero padding
void input_pad0( // Preprocess the real input sequence
const Vector& x); // Real [0:N/2-1] vector
void input_pad0( // Preprocess the complex input seq
const Vector& x_re, // [0:N/2-1] vector - Re part of input
const Vector& x_im); // [0:N/2-1] vector - Im part of input
// Output results in the form the user wants them
void real( // Give only the Re part of the result
LAStreamOut& xf_re); // [0:N-1] vector
void imag( // Give only the Im part of the result
LAStreamOut& xf_im); // [0:N-1] vector
void abs( // Give only the abs value
LAStreamOut& xf_abs); // [0:N-1] vector (power spectrum)
// Return only the half of the result
// (if the second half is unnecessary due
// to the symmetry)
void real_half( // Give only the Re part of the result
LAStreamOut& xf_re); // [0:N/2-1] vector
void imag_half( // Give only the Im part of the result
LAStreamOut& xf_im); // [0:N/2-1] vector
void abs_half( // Give only the abs value
LAStreamOut& xf_abs); // [0:N/2-1] vector
// Perform sin/cos transforms of f: R+ -> R
// Source and destination arguments of the functions
// below may point to the same vector (in that case,
// transform is computed inplace)
// Sine-transform of the function f(x)
// Integrate[ f(x) sin(kx) dx], x=0..Infinity
void sin_transform( // j=0..n-1, n=N/2
LAStreamOut& dest, // F(k) tabulated at kj = j*dk
const Vector& src // f(x) tabulated at xj = j*dr
);
// Cosine-transform of the function f(x)
// Integrate[ f(x) cos(kx) dx], x=0..Infinity
void cos_transform( // j=0..n-1, n=N/2
LAStreamOut& dest, // F(k) tabulated at kj = j*dk
const Vector& src // f(x) tabulated at xj = j*dr
);
// Inverse sine-transform of the function F(k)
// 2/pi Integrate[ F(k) sin(kx) dk], k=0..Inf
void sin_inv_transform( // j=0..n-1, n=N/2
LAStreamOut& dest, // f(x) tabulated at xj = j*dr
const Vector& src // F(k) tabulated at kj = j*dk
);
// Inverse cosine-transform of function F(k)
// 2/pi Integrate[ F(k) cos(kx) dk], k=0..Inf
void cos_inv_transform( // j=0..n-1, n=N/2
LAStreamOut& dest, // f(x) tabulated at xj = j*dr
const Vector& src // F(k) tabulated at kj = j*dk
);
// Inquires
int q_N(void) const { return N; }
int q_logN(void) const { return logN; }
double q_dr(void) const { return dr; }
double q_dk(void) const { return 2*M_PI/N/dr; }
double q_r_cutoff(void) const { return N/2 * dr; }
double q_k_cutoff(void) const { return M_PI/dr; }
};
#endif
fft_init.cc100600 1370 74 4275 6637301453 11313 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Fast Fourier Transform
*
* Initialization of the FFT packet
*
* The file defines the constructor and the destructor for the FFT class,
* allocates global arrays and initializes global data
*
************************************************************************
*/
#ifdef __GNUC__
#pragma implementation "fft.h"
#endif
#include "fft.h"
/*
*------------------------------------------------------------------------
* Constructor and Destructor
*/
FFT::FFT(const int n, const double dr)
: N(n), dr(dr)
{
assure( n > 3, "At least 4 points must be given for transforms");
assure( dr > 0, "Grid mesh in the r-space, dr, must be positive");
register int i;
for(i=1, logN=0; i < N; i *= 2, logN++)
;
assure( i == N, "No. of points has to be the exact power of two");
A = new Complex[N];
W = new Complex[N];
index_conversion_table = new short[N];
A_end = A + N;
fill_in_W();
fill_in_index_conversion_table();
}
FFT::~FFT(void)
{
assert( N > 0 && A != 0 && W != 0 && A_end == A + N );
delete A;
delete W;
delete index_conversion_table;
}
/*
*------------------------------------------------------------------------
* Fill in the index_conversion_table so that
* if j = J[m-1] * 2^(m-1) + J[m-2] * 2^(m-2) + ... + J1 * 2^1 + J0 * 2^0
* then index_conversion_table[j] =
* J0 * 2^(m-1) + J1 * 2^(m-2) + ... + J[m-2] * 2^1 + J[m-1] * 2^0
*
* m being Log2(N)
*/
void FFT::fill_in_index_conversion_table(void)
{
register int j; // j counts 0,1,2,... N-1
register int jp; // Reverse counter:
// N, N/2, N/4, N/4+N/2, N/8, N/8+N/2,
// with N identical to 0 (mod N)
register short * tablj = index_conversion_table;
register int k;
for(j=0, jp=0; *tablj++ = jp, ++j < N;)
{
for(k=N/2; k <= jp; jp -= k, k /= 2)
;
jp += k;
}
}
// Fill in the W array
// W[j] = exp( -I 2pi/N * j ), j=0..N-1
void FFT::fill_in_W(void)
{
register Complex * wj = W;
register int j;
for(j=0; j>= 1)
{
register Complex * ak;
for(ak=A; ak < A_end; ak += 2*n2_l)
{
register Complex * aj1 = ak;
register Complex * aj2 = aj1 + n2_l;
register Complex * w = W + inc; // Since j=0 case is handled explicitly
Complex ai1 = *aj1;
Complex ai2w = *aj2; // Butterfly operation in case of
*aj1++ += ai2w; // j=0 ==> W=1
*aj2++ = ai1 - ai2w;
for(; aj1 < ak+n2_l; w += inc)
{
Complex ai1 = *aj1; // w = exp(-I 2pi/N j*inc) =
Complex ai2w = *w * *aj2; // exp(-I j pi/2^l)
*aj1++ += ai2w;
*aj2++ = ai1 - ai2w; // Butterfly operation
}
}
}
}
/*
*------------------------------------------------------------------------
* The most general case of complex input without zero padding
*
* The Nussbaumer formulas are applied as they are, no further simplification
* seems possible.
*/
void FFT::input(
const Vector& x_re, // [0:N-1] vector - Re part of input
const Vector& x_im) // [0:N-1] vector - Im part of input
{
are_compatible(x_re,x_im);
if( x_re.q_lwb() != 0 || x_re.q_upb() != N-1 )
_error("Sorry, vector [%d:%d] cannot be processed.\n"
"Only [0:%d] vectors are valid",
x_re.q_lwb(), x_re.q_upb(), N-1);
register int k;
register Complex * ak;
for(ak=A,k=0; k < N; k +=8)
{
const double cu = std::real(W[ N/8 ]); // cos( pi/4 )
register int i;
i = index_conversion_table[k]; // Index being "reverse" to k
Complex x0(x_re(i),x_im(i));
i = index_conversion_table[k+1];
Complex x1(x_re(i),x_im(i));
i = index_conversion_table[k+2];
Complex x2(x_re(i),x_im(i));
i = index_conversion_table[k+3];
Complex x3(x_re(i),x_im(i));
i = index_conversion_table[k+4];
Complex x4(x_re(i),x_im(i));
i = index_conversion_table[k+5];
Complex x5(x_re(i),x_im(i));
i = index_conversion_table[k+6];
Complex x6(x_re(i),x_im(i));
i = index_conversion_table[k+7];
Complex x7(x_re(i),x_im(i));
Complex t1 = x0 + x1;
Complex t2 = x2 + x3;
Complex t3 = x4 - x5;
Complex t4 = x4 + x5;
Complex t5 = x6 + x7;
Complex t6 = x6 - x7;
Complex t7 = t1 + t2;
Complex t8 = t4 + t5;
#define m2 t1
m2 -= t2; // Forget t1 from now on
#define m3 x0
m3 -= x1; // Forget x0 from now on
Complex m7 = t3 + t6; m7 = Complex(cu* std::imag(m7),-cu* std::real(m7));
#define m4 t3 // Forget t3 from now on
m4 -= t6;
m4 *= cu;
Complex m5 = t5 - t4; m5 = Complex(- std::imag(m5), std::real(m5));
Complex m6 = x3 - x2; m6 = Complex(- std::imag(m6), std::real(m6));
#define s1 m3
Complex s2 = m3 - m4; // Forget m3 from now on
s1 += m4;
#define s3 m6 // Forget m6 from now on
Complex s4 = m6 - m7;
s3 += m7;
*ak++ = t7 + t8; // y0
*ak++ = s1 + s3; // y1
*ak++ = m2 + m5; // y2
*ak++ = s2 - s4; // y3
*ak++ = t7 - t8; // y4
*ak++ = s2 + s4; // y5
*ak++ = m2 - m5; // y6
*ak++ = s1 - s3; // y7
}
assert( ak == A_end );
complete_transform();
}
#undef m2
#undef m3
#undef m4
#undef s1
#undef s3
/*
*------------------------------------------------------------------------
* Real input sequence without zero padding
* When x[j]-s are all real, some intermediate results are either pure
* real, or pure imaginaire, which are much cheaper to compute than
* the complex ones. In Nussbauner's formulas above, t1 through t8,
* m0 through m4, s1 and s2 are all real, whilest m5 through m7, s3, and
* s4 are pure imaginaire.
*
*/
void FFT::input(
const Vector& x_re) // [0:N-1] vector - Re part of input
{
x_re.is_valid();
if( x_re.q_lwb() != 0 || x_re.q_upb() != N-1 )
_error("Sorry, vector [%d:%d] cannot be processed.\n"
"Only [0:%d] vectors are valid",
x_re.q_lwb(), x_re.q_upb(), N-1);
register int k;
register Complex * ak;
for(ak=A,k=0; k < N; k +=8)
{
const double cu = std::real(W[ N/8 ]); // cos( pi/4 )
register int i;
i = index_conversion_table[k]; // Index being "reverse" to k
double x0 = x_re(i);
i = index_conversion_table[k+1];
double x1 = x_re(i);
i = index_conversion_table[k+2];
double x2 = x_re(i);
i = index_conversion_table[k+3];
double x3 = x_re(i);
i = index_conversion_table[k+4];
double x4 = x_re(i);
i = index_conversion_table[k+5];
double x5 = x_re(i);
i = index_conversion_table[k+6];
double x6 = x_re(i);
i = index_conversion_table[k+7];
double x7 = x_re(i);
double t1 = x0 + x1;
double t2 = x2 + x3;
double t3 = x4 - x5;
double t4 = x4 + x5;
double t5 = x6 + x7;
double t6 = x6 - x7;
double t7 = t1 + t2;
double t8 = t4 + t5;
#define m2 t1
m2 -= t2; // Forget t1 from now on
#define m3 x0
m3 -= x1; // Forget x0 from now on
double m7i = -cu*(t3 + t6);
#define m4 t3 // Forget t3 from now on
m4 -= t6;
m4 *= cu;
double m5i = t5 - t4;
double m6i = x3 - x2;
#define s1 m3
double s2 = m3 - m4; // Forget m3 from now on
s1 += m4;
#define s3i m6i // Forget m6 from now on
double s4i = m6i - m7i;
s3i += m7i;
*ak++ = t7 + t8; // y0
*ak++ = Complex(s1,s3i); // y1
*ak++ = Complex(m2,m5i); // y2
*ak++ = Complex(s2,-s4i); // y3
*ak++ = t7 - t8; // y4
*ak++ = Complex(s2,s4i); // y5
*ak++ = Complex(m2,-m5i); // y6
*ak++ = Complex(s1,-s3i); // y7
}
assert( ak == A_end );
complete_transform();
}
#undef m2
#undef m3
#undef m4
#undef s1
#undef s3i
/*
*------------------------------------------------------------------------
* Complex input with zero padding
*
* Note, if index j > N/2, its "inverse", index i is odd. Since the second
* half of the input data is zero (and isn't specified), x1, x3, x5, and x7
* in the Nussbaumer formulas above are zeros, and the formulas can be
* simplified as follows
*
* t1 = x0 s1 = x0 + m4 y0 = t7 + t8
* t2 = x2 s2 = x0 - m4 y1 = s1 + s3
* t3 = x4 m2 = x0 - x2 s3 = m6 + m7 y2 = m2 + m5
* t4 = x4 m3 = x0 s4 = m6 - m7 y3 = s2 - s4
* t5 = x6 m4 = cu*(x4 - x6) y4 = t7 - t8
* t6 = x6 m5 = -I * (x4 - x6) y5 = s2 + s4
* t7 = x0 + x2 m6 = -I * x2 y6 = m2 - m5
* t8 = x4 + x6 m7 = -Isu* (x4 + x6) y7 = s1 - s3
*
* cu = su = sin( pi/4 )
*/
void FFT::input_pad0(
const Vector& x_re, // [0:N/2-1] vector - Re part of input
const Vector& x_im) // [0:N/2-1] vector - Im part of input
{
are_compatible(x_re,x_im);
if( x_re.q_lwb() != 0 || x_re.q_upb() != N/2-1 )
_error("Sorry, vector [%d:%d] cannot be processed.\n"
"Zero padding is assumed, only [0:%d] vectors are valid",
x_re.q_lwb(), x_re.q_upb(), N/2-1);
register int k;
register Complex * ak;
for(ak=A,k=0; k < N; k +=8)
{
const double cu = std::real(W[ N/8 ]); // cos( pi/4 )
register int i;
i = index_conversion_table[k]; // Index being "reverse" to k
Complex x0(x_re(i),x_im(i));
i = index_conversion_table[k+2];
Complex x2(x_re(i),x_im(i));
i = index_conversion_table[k+4];
Complex x4(x_re(i),x_im(i));
i = index_conversion_table[k+6];
Complex x6(x_re(i),x_im(i));
Complex t7 = x0 + x2;
Complex t9 = x4 - x6;
#define t8 x4 // Forget x4 from now on
t8 += x6;
Complex m2 = x0 - x2;
Complex m5(std::imag(t9),- std::real(t9));
Complex m6(std::imag(x2),- std::real(x2));
Complex m7(cu* std::imag(t8),-cu* std::real(t8));
#define m4 t9 // Forget t9 from now on
m4 *= cu;
#define s1 x0
Complex s2 = x0 - m4; // Forget x0 from now on
s1 += m4;
#define s3 m6 // Forget m6 from now on
Complex s4 = m6 - m7;
s3 += m7;
*ak++ = t7 + t8; // y0
*ak++ = s1 + s3; // y1
*ak++ = m2 + m5; // y2
*ak++ = s2 - s4; // y3
*ak++ = t7 - t8; // y4
*ak++ = s2 + s4; // y5
*ak++ = m2 - m5; // y6
*ak++ = s1 - s3; // y7
}
assert( ak == A_end );
complete_transform();
}
#undef t8
#undef m4
#undef s1
#undef s3
/*
*------------------------------------------------------------------------
* Real input sequence with zero padding
* Again, since the input is a real sequence, some intermediate results
* are also real, which makes things simpler.
*/
void FFT::input_pad0(
const Vector& x_re) // [0:N/2-1] vector - Re part of input
{
x_re.is_valid();
if( x_re.q_lwb() != 0 || x_re.q_upb() != N/2-1 )
_error("Sorry, vector [%d:%d] cannot be processed.\n"
"Zero padding is assumed, only [0:%d] vectors are valid",
x_re.q_lwb(), x_re.q_upb(), N/2-1);
register int k;
register Complex * ak;
for(ak=A,k=0; k < N; k +=8)
{
const double cu = std::real(W[ N/8 ]); // cos( pi/4 )
register int i;
i = index_conversion_table[k]; // Index being "reverse" to k
double x0 = x_re(i);
i = index_conversion_table[k+2];
double x2 = x_re(i);
i = index_conversion_table[k+4];
double x4 = x_re(i);
i = index_conversion_table[k+6];
double x6 = x_re(i);
double t7 = x0 + x2;
double t9 = x4 - x6;
#define t8 x4 // Forget x4 from now on
t8 += x6;
double m2 = x0 - x2;
double m5i = -t9;
double m7i = -cu*t8;
#define m4 t9 // Forget t9 from now on
m4 *= cu;
#define s1 x0
double s2 = x0 - m4; // Forget x0 from now on
s1 += m4;
double s3i = -x2 + m7i;
double s4i = -x2 - m7i;
*ak++ = t7 + t8; // y0
*ak++ = Complex(s1,s3i); // y1
*ak++ = Complex(m2,m5i); // y2
*ak++ = Complex(s2,-s4i); // y3
*ak++ = t7 - t8; // y4
*ak++ = Complex(s2,s4i); // y5
*ak++ = Complex(m2,-m5i); // y6
*ak++ = Complex(s1,-s3i); // y7
}
assert( ak == A_end );
complete_transform();
}
#undef t8
#undef m4
#undef s1
fft_output.cc100600 1370 74 11001 6637301564 11714 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Fast Fourier Transform
*
* Return the transformation results in the form
* the user wants them
*
* $Id: fft_output.cc,v 1.4 1998/12/20 23:08:36 oleg Exp oleg $
*
************************************************************************
*/
#include "fft.h"
/*
*-----------------------------------------------------------------------
* Give a real/imaginaire part / absolute value
* of the complex transform
*/
void FFT::real(LAStreamOut& xf_re) // must hold exactly N elements
{
for(register const Complex * ap = A; ap < A_end; )
xf_re.get() = std::real(*ap++);
if( !xf_re.eof() )
_error("The output stream was supposed to end after all %d points of the FFT "
"were placed into it",N);
}
void FFT::imag(LAStreamOut& xf_im) // must hold exactly N elements
{
for(register const Complex * ap = A; ap < A_end; )
xf_im.get() = std::imag(*ap++);
if( !xf_im.eof() )
_error("The output stream was supposed to end after all %d points of the FFT "
"were placed into it",N);
}
void FFT::abs(LAStreamOut& xf_abs) // must hold exactly N elements
{
for(register const Complex * ap = A; ap < A_end; )
xf_abs.get() = std::abs(*ap++);
if( !xf_abs.eof() )
_error("The output stream was supposed to end after all %d points of the FFT "
"were placed into it",N);
}
/*
*-----------------------------------------------------------------------
* Give only a half of the resulting transform
*/
void FFT::real_half(LAStreamOut& xf_re) // must hold exactly N/2 elements
{
for(register const Complex * ap = A; ap < A + N/2; )
xf_re.get() = std::real(*ap++);
if( !xf_re.eof() )
_error("The output stream was supposed to end after all %d points of the FFT "
"were placed into it",N/2);
}
void FFT::imag_half(LAStreamOut& xf_im) // must hold exactly N/2 elements
{
for(register const Complex * ap = A; ap < A + N/2; )
xf_im.get() = std::imag(*ap++);
if( !xf_im.eof() )
_error("The output stream was supposed to end after all %d points of the FFT "
"were placed into it",N/2);
}
void FFT::abs_half(LAStreamOut& xf_abs) // must hold exactly N/2 elements
{
for(register const Complex * ap = A; ap < A + N/2; )
xf_abs.get() = std::abs(*ap++);
if( !xf_abs.eof() )
_error("The output stream was supposed to end after all %d points of the FFT "
"were placed into it",N/2);
}
/*
*-----------------------------------------------------------------------
* Perform sin/cos transforms of a real function
* as a postprocessing of FFT
*
* Sine-transform: F(k) = Integrate[ f(x) sin(kx) dx ], x = 0..Infinity
* Cosine-transform: F(k) = Integrate[ f(x) cos(kx) dx ], x = 0..Infinity
* Inverse
* sin-transform: f(x) = 2/pi Integrate[ F(k) sin(kx) dk ], k = 0..Infinity
* cos-transform: f(x) = 2/pi Integrate[ F(k) cos(kx) dk ], k = 0..Infinity
*
* Function f(x) is tabulated over the uniform grid xj = j*dr, j=0..n-1
* Function F(k) is tabulated over the uniform grid kj = j*dk, j=0..n-1
* n=N/2
* Source and destination arguments of the functions below may point to
* the same vector (in that case, transform is computed inplace)
*/
void FFT::sin_transform(LAStreamOut& F, const Vector& f)
{
input_pad0(f);
for(register const Complex * ap = A; ap < A + N/2; )
F.get() = - std::imag(*ap++) * dr;
if( !F.eof() )
_error("The output stream was supposed to end after all %d points of the sin-tr "
"were placed into it",N/2);
}
void FFT::cos_transform(LAStreamOut& F, const Vector& f)
{
input_pad0(f);
for(register const Complex * ap = A; ap < A + N/2; )
F.get() = std::real(*ap++) * dr;
if( !F.eof() )
_error("The output stream was supposed to end after all %d points of the cos-tr "
"were placed into it",N/2);
}
void FFT::sin_inv_transform(LAStreamOut& f, const Vector& F)
{
input_pad0(F);
for(register const Complex * ap = A; ap < A + N/2; )
f.get() = - std::imag(*ap++) * 4/N/dr; // 2/pi * dk = 2/pi * 2pi/N/dr
if( !f.eof() )
_error("The output stream was supposed to end after all %d points of the inv-sin-tr "
"were placed into it",N/2);
}
void FFT::cos_inv_transform(LAStreamOut& f, const Vector& F)
{
input_pad0(F);
for(register const Complex * ap = A; ap < A + N/2; )
f.get() = std::real(*ap++) * 4/N/dr;
if( !f.eof() )
_error("The output stream was supposed to end after all %d points of the inv-cos-tr "
"were placed into it",N/2);
}
math_num.h100600 1370 74 6141 6605771306 11160 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Numerical Math Package
*
* The present package implements various algorithms of Numerical Math
*
* $Id: math_num.h,v 4.1 1998/10/04 22:12:22 oleg Exp oleg $
*
************************************************************************
*/
#ifndef __GNUC__
#pragma once
#endif
#ifndef _math_num_h
#define _math_num_h 1
#if defined(__GNUC__)
#pragma interface
#endif
#include "myenv.h"
#include
#include
#include "builtin.h"
#include "std.h"
/*
*------------------------------------------------------------------------
* Some constants
* Compile and run the program epsilon.c to determine the values below for
* your computer
*/
//#define EPSILON 2.22045e-16 // DBL_EPSILON
//#define SQRT_EPSILON 1.49012e-08
/*
*------------------------------------------------------------------------
* Brent's minimum and zero finders for
* a function of a single argument
*/
// A function of a single argument
struct UnivariateFunctor
{
virtual double operator() (const double x) = 0;
};
// Obtain a zero of function f
// over the interval [ax,bx] with the
// accuracy tol.
double zeroin(const double ax, const double bx,
UnivariateFunctor& f, const double tol=DBL_EPSILON);
// Find a minimum of function f
// over the interval [a,b] with the
// accuracy tol.
// Returns an approx. to the min location
double fminbr(const double a, const double b,
UnivariateFunctor& f, const double tol=DBL_EPSILON);
class Vector; // Opaque class used by the routines below
/*
*------------------------------------------------------------------------
* Interpolation of the function
* specified in the tabular form
*/
// Aitken-Lagrange interpolation to the
// point q over the table of function values
// y[i] = y(x[i]), i = y.lwb..y.upb
// Uniform mesh x[i] = x0 + s*(i-y.lwb)
double ali(const double q, const double x0, const double s, const Vector& y);
// Nonuniform grid with nodes in x[i]
double ali(const double q, const Vector& x, const Vector& y);
/*
*------------------------------------------------------------------------
* Multi-dimensional minimization
*/
// A function of a _vector_ argument
struct MultivariateFunctor
{
virtual double operator() (const Vector& x) = 0;
};
// Find a local minimum of a given
// function by the Hooke-Jeeves method
double hjmin( // Return the function value at min
Vector& b, // Input: initial guess to min loc
// Output: loc for the min found
Vector& h, // Input: initial values for the
// steps along each b(i)
// Output: final steps right before
// the termination
MultivariateFunctor& f // A function being optimized
);
// The same as above with the only difference
// initial steps are given to be the same
// along every direction. The final steps
// aren't reported back though
double hjmin(Vector& b, const double h0,
MultivariateFunctor& f);
#endif
myenv.cc100600 1370 74 16304 6641511643 10662 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
* Service C++ functions
* that support the standard environment for me
*
* $Id: myenv.cc,v 2.3 1998/12/20 23:29:08 oleg Exp oleg $
*/
#if defined(__GNUC__)
#pragma implementation
//#pragma implementation "Logger.h"
#endif
#include "myenv.h"
#include
#include
#include
#include
#include
#include
extern "C"
{
#if defined(macintosh)
#include
#include
#elif defined(WIN32)
#include
#include
#define stat _stat
#define STDERR_FILENO 2
#else
#include
#include
#include
#endif
int stat(const char * path, struct stat * buf);
}
//#include "Logger.h"
/*
*-----------------------------------------------------------------------
* Some global constant pertaining to input/output
*/
const char _Minuses [] = "\
-------------------------------------------------------------------------------";
const char _Asteriscs [] = "\
*******************************************************************************";
const char _Equals [] = "\
===============================================================================";
//------------------------------------------------------------------------
// A better getenv
//
// It works just as a regular getenv: searches the process' environment for
// a string of the form name=value and, if the string is present, returns
// a pointer to the 'value' part of it.
// If the string is not present, the default_value is returned, unless it
// is nil.
// If the default_value was nil and the string wasn't found,
// the function prints the message that the name wasn't
// found, and aborts the program
const char * xgetenv(const char * name, const char * default_value)
{
const char * env_value = ::getenv(name);
if( env_value != 0 )
return env_value;
else if( default_value != 0 )
return default_value;
else
return _error("xgetenv: env variable '%s' wasn't found, but was required",
name), (const char *)0;
}
/*
*------------------------------------------------------------------------
* Print an error message at stderr and abort
* Synopsis
* volatile void _error(const char * message,... );
* Message may contain format control sequences %x. Items to print
* with the control sequences are to be passed as additional arguments to
* the function call.
*/
void _error(const char * message,...)
{
va_list args;
va_start(args,message); /* Init 'args' to the beginning of */
/* the variable length list of args*/
fprintf(stderr,"\n_error:\n");
vfprintf(stderr,message,args);
fputs("\n",stderr);
#ifdef __MWERKS__
exit(4);
#else
abort();
#endif
}
/*
*------------------------------------------------------------------------
* Print a message at stderr
* Synopsis
* void message(const char * text,... );
* It looks and acts like printf(), only prints on stderr
* (which is usually unbuffered...)
*/
void message(const char * text,...)
{
va_list args;
va_start(args,text); /* Init 'args' to the beginning of */
/* the variable length list of args*/
vfprintf(stderr,text,args);
}
//------------------------------------------------------------------------
// A logging service
//
// It filters and logs various system activities onto stderr
// (unless it is set to log to something different)
#if 0
ostream_withassign Logger::log_stream(new filebuf(STDERR_FILENO));
// Set the log to append to a specified file
void Logger::set_log(const char log_file_name [])
{
filebuf * const new_filebuf = new filebuf();
if( new_filebuf->open(log_file_name,ios::out|ios::ate) == 0 )
perror("Log file open error"),
_error("Failed to open the log file '%s' because of the error above",
log_file_name);
log_stream = new_filebuf; // set new streambuf and delete the old one
}
#endif
//------------------------------------------------------------------------
// Obtaining the size of a file
// Default action when the file wasn't found/can't be
// accessed
size_t GFS_Default::operator () (const char * file_name)
{
perror("getting file status");
_error("Failed to get status of the file <%s> because of the error "
"above",file_name);
return (size_t)EOF;
}
GFS_Default GFS_default;
size_t get_file_size(const char * file_name, GFS_Default& on_error)
{
struct stat file_status;
if( stat(file_name,&file_status) != 0 )
return on_error(file_name);
return file_status.st_size;
}
//------------------------------------------------------------------------
// Patches to the standard environment
// Like strncpy(), but ALWAYS terminates
// the destination string
char * xstrncpy(char * dest, const char * src, const int len)
{
strncpy(dest,src,len);
dest[len] = '\0';
return dest;
}
// Convert char c to lower case
// Unlike traditional tolower(), it
// applies conversion only if c is a letter
// in uppercase
static inline int to_lower(const char c)
{
return isupper(c) ? (c - 'A') + 'a' : c;
}
// Return TRUE if string s1 starts with s2
// i.e., s2 is the prefix of s1
// Case doesn't matter
bool does_start_with_ci(const char * s1, const char * s2)
{
while( *s2 != '\0' ) // Alas, stricmp is not standard
if( *s1 == '\0' )
return false; // s1 is shorter than s2
else if( to_lower(*s1++) != to_lower(*s2++) )
return false;
return true;
}
// Convenient pow functions that are often missing
// They both compute x^y where y is an integer
// This code is based on target.c of GNU's F77 code, and sets
// the results in controversial cases as that code.
double pow(long x, long y)
{
if( x == 0 )
return 0;
if( y == 0 )
return 1;
if( x == 1 )
return 1;
double multiplier = y > 0 ? x : (1.0/x);
if( y < 0 )
y = -y;
while( (y & 1) == 0 )
{
multiplier *= multiplier;
y >>= 1;
}
double accum = multiplier;
y >>= 1;
while( y != 0 )
{
multiplier *= multiplier;
if( (y & 1) == 1)
accum *= multiplier;
y >>= 1;
}
return accum;
}
double pow(double x, long y)
{
if( x == 0 )
return 0;
if( y == 0 )
return 1;
if( x == 1 )
return 1;
if( y < 0 )
y = -y, x = 1.0/x;
while( (y & 1) == 0 )
{
x *= x;
y >>= 1;
}
double accum = x;
y >>= 1;
while( y != 0 )
{
x *= x;
if( (y & 1) == 1)
accum *= x;
y >>= 1;
}
return accum;
}
#if !defined(__GNUC__) || ((__GNUC__ == 2) && (__GNUC_MINOR__ > 7))
#include
// libg++ nifty timing functions
// Very rough and dirty implementation for
// platforms w/o libg++
static time_t time_set;
double start_timer(void)
{
return time_set = time(0);
}
// return_elapsed_time(last_time) returns
// process time (in secs) since Last_Time
// If Last_time == 0.0, return time since
// the last call to start_timer()
double return_elapsed_time(const double last_time)
{
time_t new_time = time(0);
if( time_set == 0 )
return -1; // timer wasn't started
return new_time - (last_time == 0.0 ? time_set : last_time);
}
#endif
myenv.h100600 1370 74 6501 6637304556 10512 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
// ************************************************************************
//
// A standard environment
// I am accustomed to
//
// $Id: myenv.h,v 2.2 1998/12/20 23:34:06 oleg Exp oleg $
#ifndef __GNUC__
#pragma once
#endif
#ifndef _myenv_h
#define _myenv_h
#ifdef __GNUC__
#pragma interface
#endif
#include
//------------------------------------------------------------------------
// Patches to the standard environment
#if 0 // uncomment if the compiler sucks...
typedef int bool;
#define false 0
#define true (!false)
//enum bool {false,true};
#endif
// Like strncpy(), but ALWAYS terminates
// the destination string
char * xstrncpy(char * dest, const char * src, const int len);
inline long int sqr(const int x) { return x*x; }
double pow(double, long);
double pow(long, long);
// Works just as a regular getenv:
// searches the process' environment for a string of the form
// name=value and, if the string is
// present, returns a pointer to the 'value' part of the
// string.
// If the string is not present, the default_value is
// returned, unless it is nil.
// If the default_value was nil and the string wasn't found,
// the function prints the message that the name wasn't
// found, and aborts the program
const char * xgetenv(const char * name, const char * default_value);
// Return TRUE if string s1 starts with s2
// i.e., s2 is a prefix of s1
// Case doesn't matter
bool does_start_with_ci(const char * s1, const char * s2);
// Obtain the size of an existing file
// If you don't want the function to abort when
// the file wasn't found/couldn't be accessed,
// supply your own default action by subclassing from
// GFS_Default and overriding 'operator ()'
extern struct GFS_Default { virtual size_t operator () (const char * file_name); }
GFS_default;
size_t get_file_size(const char * file_name,
GFS_Default& on_error = GFS_default);
// libg++ nifty timing functions
// return_elapsed_time(Last_time) returns
// process time (in secs) since Last_Time
// If Last_time == 0.0, return time since
// the last call to start_timer()
#if !defined(__GNUC__) || ((__GNUC__ == 2) && (__GNUC_MINOR__ > 7))
double start_timer(void);
double return_elapsed_time(const double Last_Time);
#endif
//------------------------------------------------------------------------
// Assertions, aborts, and related stderr printing
// Print an error message on stderr and
// abort, arguments like those of printf()
void _error(const char * message,...);
// Print a message on stderr
// It looks and acts like printf(), only
// prints on stderr (which is unbuffered...)
void message(const char * message,...);
#ifndef assert
#define assert(ex) \
(void)((ex) ? 1 : \
(_error("Failed assertion " #ex " at line %d of `%s'.\n", \
__LINE__, __FILE__), 0))
#define assertval(ex) assert(ex)
#endif
#define assure(expr,message) \
if (expr) {;} \
else _error("%s\n at line %d of '%s'.",message,__LINE__, __FILE__)
// Strings of symbols
// They may be used as a delimiting lines
extern const char _Minuses [];
extern const char _Asteriscs [];
extern const char _Equals [];
#endif
std.h100600 1370 74 2335 6514233151 10131 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
/*
Copyright (C) 1988, 1992 Free Software Foundation
written by Doug Lea (dl@rocky.oswego.edu)
This file is part of the GNU C++ Library. This library is free
software; you can redistribute it and/or modify it under the terms of
the GNU Library General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version. This library is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _std_h
#define _std_h 1
#if defined(__GNUC__)
#include <_G_config.h>
#endif
#include
#include
#if defined(unix)
#include
#elif defined(WIN32)
#else
#endif
#include
#include
#include
#if defined(__GNUC__)
extern "C" {
int strcasecmp _G_ARGS((const char*, const char*));
}
#endif
#endif
vfft.cc100600 1370 74 33633 6640003044 10463 0ustar olegdiv60// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Verify the Fast Fourier Transform Package
*
* $Id: vfft.cc,v 1.5 1998/12/22 20:50:44 oleg Exp oleg $
*
************************************************************************
*/
#include "fft.h"
#include
#include
/*
*------------------------------------------------------------------------
* Timing the program execution
*/
#include
static clock_t clock_acc;
static void start_timing(void)
{
clock_acc = clock();
}
static void print_timing(const char * header)
{
register clock_t old_tick = clock_acc;
register float timing = (clock_acc=clock(),clock_acc - old_tick )
/(float)CLOCKS_PER_SEC; // In secs
printf("\nIt took %.2f sec to perform %s\n",timing,header);
}
/*
*-----------------------------------------------------------------------
*/
// Simplified printing a vector
static void print_seq(const char * header, const Vector& v)
{
LAStreamIn vs(v);
printf("\n%s\t",header);
while( !vs.eof() )
printf("%7.4f ",vs.get());
printf("\n");
}
/*
*-----------------------------------------------------------------------
* Check FFT of the Arithmetical Progression sequence
* x[j] = j
* The analytical transform is
* SUM{ j*W^(kj) } = N/(W^k - 1), k > 0,
* N*(N-1)/2, k = 0
*/
static void test_ap_series(const int N)
{
cout << "\n\nVerify the computed FFT of the AP series x[j]=j\n";
cout << "j = 0.." << N-1 << endl;
Vector xre(0,N-1);
Vector xim = zero(xre);
for(register int j=0; j l
* N, k = l
*/
static void test_orth(const int N, const int l)
{
cout << "\n\nVerify the computed FFT for x[j] = W^(-l*j)\n";
cout << "j = 0.." << N-1 << ", l=" << l << endl;
Vector xre(0,N-1);
Vector xim(xre);
register int j;
for(j=0; j 0 and even
* 2*W^k/(W^k - 1)^2 - N/2 * 1/(W^k - 1), k being odd
* N/2 * (N/2-1)/2, k = 0
*/
static void test_ap_series_pad0(const int N)
{
cout << "\n\nVerify the computed FFT of the truncated AP sequence x[j]=j\n";
cout << "j = 0.." << N/2-1 << ", with N=" << N << endl;
Vector xre(0,N/2-1);
Vector xim(xre);
for(register int j=0; j 2a^3 k / (1 + (ak)^2)^2
* r*exp( -r/a ) <=== cos-transform ===> a^2 (1 - (ak)^2)/(1 + (ak)^2)^2
*/
static void test_lorentzian(void)
{
const double R = 20; // Cutoff distance
const int n = 512; // No. of grids
const double a = 4; // Constant a, see above
const double dr = R/n, // Grid meshes
dk = M_PI/R;
cout << "\n\nVerify the sin/cos transform for the following example\n";
cout << "\tr*exp( -r/a )\t<=== sin-transform ===>\t2a^3 k/(1 + (ak)^2)^2\n";
cout << "\tr*exp( -r/a )\t<=== cos-transform ===>\t"
"a^2 (1-(ak)^2)/(1 + (ak)^2)^2" << endl;
printf("\nParameter a is %2f",a);
printf("\nNo. of grids %d",n);
printf("\nGrid mesh in the r-space dr = %.3f",dr);
printf("\nGrid mesh in the k-space dk = %.3f\n",dk);
FFT fft(2*n,dr);
cout << "\nCheck out the inquires to FFT package about N, dr, dk, cutoffs"
<< endl;
assert( fft.q_N() == 2*n );
assert( fft.q_dr() == dr );
assert( fft.q_dk() == dk );
assert( fft.q_r_cutoff() == R );
assert( fft.q_k_cutoff() == dk*n );
Vector xr(0,n-1); // Tabulate the source function
register int j;
for(j=0; j sqrt(a*pi) exp(-a*k^2)
*/
static void test_gaussian(void)
{
const double R = 20; // Cutoff distance
const int n = 512; // No. of grids
const double a = 4; // Constant a, see above
const double dr = R/n, // Grid meshes
dk = M_PI/R;
cout << "\n\nVerify the sin/cos transform for the following example\n";
cout << "\texp( -r^2/4a )\t<=== cos-transform ===> sqrt(a*pi) exp(-a*k^2)\n";
printf("\nParameter a is %.2f",a);
printf("\nNo. of grids %d",n);
printf("\nGrid mesh in the r-space dr = %.3f",dr);
printf("\nGrid mesh in the k-space dk = %.3f\n",dk);
FFT fft(2*n,dr);
cout << "\nCheck out the inquires to FFT package about N, dr, dk, cutoffs"
<< endl;
assert( fft.q_N() == 2*n );
assert( fft.q_dr() == dr );
assert( fft.q_dk() == dk );
assert( fft.q_r_cutoff() == R );
assert( fft.q_k_cutoff() == dk*n );
Vector xr(0,n-1); // Tabulate the source function
register int j;
for(j=0; j 2a^3 k/(1 + (ak)^2)^2
r*exp( -r/a ) <=== cos-transform ===> a^2 (1-(ak)^2)/(1 + (ak)^2)^2
Parameter a is 4.000000
No. of grids 512
Grid mesh in the r-space dr = 0.039
Grid mesh in the k-space dk = 0.157
Check out the inquires to FFT package about N, dr, dk, cutoffs
It took 0.01 sec to perform Sine transform
It took 0.02 sec to perform Cosine transform
Comparison of two Matrices:
Computed and Exact sin-transform
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 0.312458
occured at the point (1,1)
Matrix 1 element is 10.6476
Matrix 2 element is 10.3351
Absolute error v2[i]-v1[i] -0.312458
Relative error -0.0297824
||Matrix 1|| 26.1276
||Matrix 2|| 23.5973
||Matrix1-Matrix2|| 4.69511
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 0.189089
Comparison of two Matrices:
Computed and Exact cos-transform
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 0.649606
occured at the point (0,1)
Matrix 1 element is 15.3504
Matrix 2 element is 16
Absolute error v2[i]-v1[i] 0.649606
Relative error 0.0414416
||Matrix 1|| 34.543
||Matrix 2|| 33.8396
||Matrix1-Matrix2|| 3.01891
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 0.0882996
Comparison of two Matrices:
Computed cos-transform with DC component removed, and exact result
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 0.651867
occured at the point (0,1)
Matrix 1 element is 15.3481
Matrix 2 element is 16
Absolute error v2[i]-v1[i] 0.651867
Relative error 0.0415889
||Matrix 1|| 34.9727
||Matrix 2|| 33.8396
||Matrix1-Matrix2|| 3.01892
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 0.0877554
It took 0.02 sec to perform Inverse sine transform
It took 0.01 sec to perform Inverse cosine transform
Comparison of two Matrices:
Computed inverse sin-transform vs the original function
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 5.96046e-08
occured at the point (221,1)
Matrix 1 element is 0.997371
Matrix 2 element is 0.997371
Absolute error v2[i]-v1[i] 5.96046e-08
Relative error 5.97618e-08
||Matrix 1|| 392.97
||Matrix 2|| 392.97
||Matrix1-Matrix2|| 1.12206e-05
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 2.85533e-08
Comparison of two Matrices:
Computed inverse cos-transform vs the original function
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 0.767671
occured at the point (58,1)
Matrix 1 element is 2.05355
Matrix 2 element is 1.28588
Absolute error v2[i]-v1[i] -0.767671
Relative error -0.459761
||Matrix 1|| 785.94
||Matrix 2|| 392.97
||Matrix1-Matrix2|| 392.97
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 0.707107
Comparison of two Matrices:
Computed inverse cos-transform with DC component removed,
and the original function
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 0.135816
occured at the point (55,1)
Matrix 1 element is 1.11981
Matrix 2 element is 1.25562
Absolute error v2[i]-v1[i] 0.135816
Relative error 0.11435
||Matrix 1|| 324.137
||Matrix 2|| 392.97
||Matrix1-Matrix2|| 69.4601
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 0.194622
Done
Verify the sin/cos transform for the following example
exp( -r^2/4a ) <=== cos-transform ===> sqrt(a*pi) exp(-a*k^2)
Parameter a is 4.00
No. of grids 512
Grid mesh in the r-space dr = 0.039
Grid mesh in the k-space dk = 0.157
Check out the inquires to FFT package about N, dr, dk, cutoffs
It took 0.02 sec to perform Cosine transform
Comparison of two Matrices:
Computed and Exact cos-transform
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 0.0195313
occured at the point (18,1)
Matrix 1 element is 0.0195313
Matrix 2 element is 4.5914e-14
Absolute error v2[i]-v1[i] -0.0195313
Relative error -2
||Matrix 1|| 21.7725
||Matrix 2|| 11.7725
||Matrix1-Matrix2|| 10
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 0.624616
Comparison of two Matrices:
Computed with DC removed, and Exact cos-transform
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 2.23517e-08
occured at the point (350,1)
Matrix 1 element is -2.23517e-08
Matrix 2 element is 0
Absolute error v2[i]-v1[i] 2.23517e-08
Relative error 0.223517
||Matrix 1|| 11.7725
||Matrix 2|| 11.7725
||Matrix1-Matrix2|| 3.91315e-06
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 3.32399e-07
It took 0.02 sec to perform Inverse cosine transform
Comparison of two Matrices:
Computed inverse cos-transform vs the original function
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 0.177245
occured at the point (2,1)
Matrix 1 element is 1.17686
Matrix 2 element is 0.999619
Absolute error v2[i]-v1[i] -0.177245
Relative error -0.162873
||Matrix 1|| 181.999
||Matrix 2|| 91.2496
||Matrix1-Matrix2|| 90.7496
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 0.704198
Comparison of two Matrices:
Computed inverse with DC removed vs the original
Matrix 0:511x1:1 is not engaged
Matrix 0:511x1:1 is not engaged
Maximal discrepancy 3.57628e-07
occured at the point (0,1)
Matrix 1 element is 1
Matrix 2 element is 1
Absolute error v2[i]-v1[i] 3.57628e-07
Relative error 3.57628e-07
||Matrix 1|| 91.2496
||Matrix 2|| 91.2496
||Matrix1-Matrix2|| 6.16255e-06
||Matrix1-Matrix2||/sqrt(||Matrix1|| ||Matrix2||) 6.75351e-08
Done
check speed of different kind of transforms
j = 0..2047 for 100 times
Performing Complex FFT
It took 4.28 sec to perform Complex Fourier transform
Performing FFT of a REAL sequence
It took 3.48 sec to perform "Real" Fourier transform
Performing Complex FFT of a half sequence padded by zeros
It took 3.86 sec to perform Complex Fourier transform
Performing FFT of a REAL half-sequence padded by zeros
It took 3.38 sec to perform "Real" Fourier transform
Performing FFT of a REAL padded half-sequence and getting half
It took 3.24 sec to perform "Real" Fourier transform
Done
Compilation finished at Fri Dec 25 23:27:07
g++.2.7.2100755 1370 74 711 6307325702 10125 0ustar olegdiv60#!/bin/sh
# GNU C++ compilation
# Note, ${foo:+bar} is just a weird way of commenting out bar
gcc -c -O -pipe -W -Wall -Wpointer-arith -ffor-scope -Woverloaded-virtual \
-Wstrict-prototypes -Wmissing-prototypes ${foo:+'-Winline -Wredundant-decls'} \
-finline-functions -fforce-mem ${foo:+'-funsigned-char -fshort-enums'} \
-fforce-addr -fstrict-prototype -felide-constructors -fno-implicit-templates \
-fomit-frame-pointer -freg-struct-return \
-I. -I- $*
g++.2.8.1100700 1370 74 1161 6635777733 10156 0ustar olegdiv60#!/bin/sh
# GNU C++ compilation
# Note, ${foo:+bar} is just a weird way of commenting out bar
gcc -c -pipe -W -Wall -Wpointer-arith -ffor-scope -Woverloaded-virtual \
-Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \
${foo:+'-Wold-style-cast'} -Wbad-function-cast -Wwrite-strings \
${foo:+'-Winline -Wredundant-decls'} -fno-rtti -fno-exceptions \
-finline-functions -fforce-mem ${foo:+'-funsigned-char -fshort-enums'} \
-fforce-addr -fstrict-prototype -felide-constructors ${foo:+'-fno-implicit-templates'} \
-fomit-frame-pointer ${foo:+'-fvtable-thunks'} ${foo:+'-Weffc++'} -freg-struct-return \
-I. -I- $*
00Need-LinAlg100600 1370 74 742 6641533206 11255 0ustar olegdiv60 You need LinAlg 4.3 to compile this FFT v1.2 package
Download LinAlg.tar.gz from the same place you got fft.tar.gz
Make sure the version of the LinAlg package is 4.3
Untar LinAlg.tar.gz first, which makes a directory LinAlg
cd LinAlg and untar this fft.tar.gz archive within that directory
Look through the Makefile and make adjustments if necessary
(in compilation flags, standard libraries to link with, etc)
make dist-clean lib check-all
Disregard all compiler warnings