Skip to content
Snippets Groups Projects
Commit 804c60ba authored by Christophe Geuzaine's avatar Christophe Geuzaine
Browse files

Arpack now uses the same configuration file than Lanczsos (eigen.par)
parent ce61f9b9
No related branches found
No related tags found
No related merge requests found
#define RCSID "$Id: Arpack.c,v 1.10 2005-07-08 21:54:52 geuzaine Exp $"
#define RCSID "$Id: Arpack.c,v 1.11 2005-07-11 15:00:17 geuzaine Exp $"
/*
* Copyright (C) 1997-2005 P. Dular, C. Geuzaine
*
......@@ -24,8 +24,8 @@
#include "GetDP.h"
#include "DofData.h"
#include "CurrentData.h"
#include "Numeric.h"
#include "EigenPar.h"
#if !defined(HAVE_LAPACK)
......@@ -70,17 +70,31 @@ static void GetDP2Arpack(gVector *in, complex_16 *out)
void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
double shift_r, double shift_i){
struct EigenPar eigenpar;
struct Solution Solution_S;
gVector v1, v2;
int j, k, l, newsol;
double tmp;
double tmp, d1, d2, dmax;
gMatrix *K = &DofData_P->M1; /* matrix associated with terms with no Dt nor DtDt */
gMatrix *M = &DofData_P->M3; /* matrix associated with DtDt terms */
int n = DofData_P->NbrDof / gCOMPLEX_INCREMENT; /* size of the system */
int ido = 0;
/* Arpack parameters: see below for explanation */
int ido, nev, ncv, ldv, iparam[11], ipntr[14], lworkl, info, ldz;
char bmat, *which, howmny;
double tol, *rwork;
unsigned int rvec, *select;
complex_16 *resid, *v, *workd, *workl, *d, *z, sigma, *workev;
GetDP_Begin("EigenSolve");
/* Get eigenproblem parameters */
EigenParRead("eigen.par", &eigenpar);
EigenParWrite("eigen.par", &eigenpar);
EigenParPrint(&eigenpar);
ido = 0;
/* Reverse communication flag. IDO must be zero on the first
call to znaupd. IDO will be set internally to
indicate the type of operation to be performed. Control is
......@@ -112,13 +126,13 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
the "shift-and-invert" mode, the vector M * X is already
available and does not need to be recomputed in forming OP*X. */
char bmat = 'I';
bmat = 'I';
/* BMAT specifies the type of the matrix B that defines the
semi-inner product for the operator OP.
BMAT = 'I' -> standard eigenvalue problem A*x = lambda*x
BMAT = 'G' -> generalized eigenvalue problem A*x = lambda*M*x */
char *which = "LM";
which = "LM";
/* Which eigenvalues we want:
SM = smallest magnitude ( magnitude = absolute value )
LM = largest magnitude
......@@ -127,18 +141,24 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
SI = smallest imaginary part
LI = largest imaginary part */
int nev = NumEigenvalues;
nev = NumEigenvalues;
/* Number of eigenvalues of OP to be computed. 0 < NEV < N-1.
Therefore, you'll be able to compute AT MOST n-2 eigenvalues! */
double tol = 1.e-4;
/* sanity check */
if(nev >= n-1){
Msg(WARNING, "NumEigenvalues too large (%d < %d): setting to %d", nev, n-1, n-2);
nev = n-2;
}
tol = eigenpar.prec; /* 1.e-4; */
/* Stopping criteria: the relative accuracy of the Ritz value
is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I))
where ABS(RITZ(I)) is the magnitude when RITZ(I) is complex.
DEFAULT = dlamch('EPS') (machine precision as computed
by the LAPACK auxiliary subroutine dlamch). */
complex_16 *resid = (complex_16*)Malloc(n * sizeof(complex_16));
resid = (complex_16*)Malloc(n * sizeof(complex_16));
/* On INPUT:
If INFO .EQ. 0, a random initial residual vector is used.
If INFO .NE. 0, RESID contains the initial residual vector,
......@@ -146,7 +166,7 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
On OUTPUT:
RESID contains the final residual vector. */
int ncv = NumEigenvalues * 2;
ncv = eigenpar.size; /* Rule of thumb: NumEigenvalues * 2; */
/* Number of columns of the matrix V. NCV must satisfy the two
inequalities 1 <= NCV-NEV and NCV <= N.
This will indicate how many Arnoldi vectors are generated
......@@ -156,15 +176,35 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
iteration. Most of the cost in generating each Arnoldi vector is
in the matrix-vector operation OP*x. */
complex_16 *v = (complex_16*)Malloc(n * ncv * sizeof(complex_16));
/* sanity checks */
if(ncv <= nev){
Msg(WARNING, "Krylov space size too small (%d <= %d), setting to %d", ncv, nev, nev*2);
ncv = nev * 2;
}
if(ncv > n){
Msg(WARNING, "Krylov space size too large (%d > %d), setting to %d", ncv, n, n);
ncv = n;
}
v = (complex_16*)Malloc(n * ncv * sizeof(complex_16));
/* At the end of calculations, here will be stored the Arnoldi basis
vectors */
int ldv = n;
ldv = n;
/* Leading dimension of "v". In our case, the number of lines of
"v". */
int iparam[11] = {1, 0, 10000, 1, 0, 0, 1, 0, 0, 0, 0};
iparam[0] = 1;
iparam[1] = 0;
iparam[2] = 10000;
iparam[3] = 1;
iparam[4] = 0;
iparam[5] = 0;
iparam[6] = 1;
iparam[7] = 0;
iparam[8] = 0;
iparam[9] = 0;
iparam[10] = 0;
/* iparam[0] = ISHIFT: method for selecting the implicit shifts.
The shifts selected at each iteration are used to filter out
the components of the unwanted eigenvector.
......@@ -224,7 +264,7 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
NUMOPB = total number of B*x operations if BMAT='G',
NUMREO = total number of steps of re-orthogonalization. */
int ipntr[14];
ipntr[0] = 0;
/* Pointer to mark the starting locations in the WORKD and WORKL
arrays for matrices/vectors used by the Arnoldi iteration.
-------------------------------------------------------------
......@@ -253,24 +293,24 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
of the upper Hessenberg matrix H. Only referenced by
zneupd if RVEC = .TRUE. See Remark 2 below. */
complex_16 *workd = (complex_16*)Malloc(3 * n * sizeof(complex_16));
workd = (complex_16*)Malloc(3 * n * sizeof(complex_16));
/* Distributed array to be used in the basic Arnoldi iteration
for reverse communication. The user should not use WORKD
as temporary workspace during the iteration !!!!!!!!!!
See Data Distribution Note below. */
int lworkl = 3*ncv*ncv + 5*ncv;
lworkl = 3*ncv*ncv + 5*ncv;
/* Dimension of the "workl" vector (see below). On must have:
lworkl >= 3*ncv*ncv + 5*ncv */
complex_16 *workl = (complex_16*)Malloc(lworkl * sizeof(complex_16));
workl = (complex_16*)Malloc(lworkl * sizeof(complex_16));
/* Private (replicated) array on each PE or array allocated on
the front end. See Data Distribution Note below. */
double *rwork = (double*)Malloc(ncv * sizeof(double));
rwork = (double*)Malloc(ncv * sizeof(double));
/* Used as workspace */
int info = 0;
info = 0;
/* If INFO .EQ. 0, a randomly initial residual vector is used.
If INFO .NE. 0, RESID contains the initial residual vector,
possibly from a previous run.
......@@ -304,22 +344,22 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
IPARAM(5) returns the size of the current Arnoldi
factorization. */
unsigned int rvec = 1; /* .true. */
rvec = 1; /* .true. */
/* If we want Ritz vectors to be computed as well. In our case, no. */
char howmny = 'A';
howmny = 'A';
/* What do we want: Ritz or Schur vectors? In our example, we won't use them,
anyway. For Schur, choose: howmny = 'P' */
unsigned int *select = (unsigned int*)Malloc(ncv * sizeof(unsigned int));
select = (unsigned int*)Malloc(ncv * sizeof(unsigned int));
/* Internal workspace */
complex_16 *d = (complex_16*)Malloc(nev * sizeof(complex_16));
d = (complex_16*)Malloc(nev * sizeof(complex_16));
/* Vector containing the "nev" eigenvalues computed.
VERY IMPORTANT: on line 69 of zneupd.f they say it should be nev+1;
this is wrong, for see line 283 where it is declared as d(nev) */
complex_16 *z = (complex_16*)Malloc(n * nev * sizeof(complex_16));
z = (complex_16*)Malloc(n * nev * sizeof(complex_16));
/* On exit, if RVEC = .TRUE. and HOWMNY = 'A', then the columns of
Z represents approximate eigenvectors (Ritz vectors) corresponding
to the NCONV=IPARAM(5) Ritz values for eigensystem
......@@ -332,22 +372,18 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
basis array V computed by ZNAUPD. In this case the Arnoldi basis
will be destroyed and overwritten with the eigenvector basis. */
int ldz = n;
ldz = n;
/* Leading dimension of "z". In our case, the number of lines of "z". */
complex_16 sigma = {0., 0.,};
sigma.re = 0.;
sigma.im = 0.;
/* The shift. Not used in this case: we deal with the shift "by
hand". */
complex_16 *workev = (complex_16*)Malloc(2 * ncv * sizeof(complex_16));
workev = (complex_16*)Malloc(2 * ncv * sizeof(complex_16));
/* Workspace */
GetDP_Begin("EigenSolve");
/* Sanity checks */
if(nev >= n-1)
Msg(ERROR, "NumEigenvalues too large (must be < %d)", n-1);
if(!DofData_P->Flag_Init[1] || !DofData_P->Flag_Init[3])
Msg(ERROR, "No System available for EigenSolve: check 'DtDt' and 'GenerateSeparate'");
......@@ -444,6 +480,23 @@ void EigenSolve (struct DofData * DofData_P, int NumEigenvalues,
LinAlg_SetComplexInVector(z[k*n+j].re, z[k*n+j].im,
&DofData_P->CurrentSolution->x, l, l+1);
}
/* Normalize eigenvector in L2 norm */
dmax = 0.0 ;
for(l = 0; l < DofData_P->NbrDof; l+=gCOMPLEX_INCREMENT){
LinAlg_GetComplexInVector(&d1, &d2, &DofData_P->CurrentSolution->x, l, l+1);
tmp = SQU(d1) + SQU(d2);
if(tmp > dmax){
dmax = tmp;
}
}
if(dmax > 1.e-16){
LinAlg_ProdVectorDouble(&DofData_P->CurrentSolution->x, 1./sqrt(dmax),
&DofData_P->CurrentSolution->x);
}
else{
Msg(WARNING, "Unable to norm eigenvector %d: norm = %g", k, dmax);
}
}
/* deallocate */
......
#define RCSID "$Id: EigenPar.c,v 1.1 2005-07-11 15:00:17 geuzaine Exp $"
/*
* Copyright (C) 1997-2005 P. Dular, C. Geuzaine
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Please report all bugs and problems to <getdp@geuz.org>.
*
* Contributor(s):
*/
#include "GetDP.h"
#include "CurrentData.h"
#include "EigenPar.h"
void EigenParRead(char *filename, struct EigenPar *par){
char path[MAX_FILE_NAME_LENGTH];
FILE *fp;
/* set some defaults */
par->prec = 1.e-4;
par->reortho = 0;
par->size = 50;
/* try to read parameters from file */
strcpy(path, Name_Path);
strcat(path, filename);
fp = fopen(path, "r+t");
if(fp) {
Msg(INFO, "Loading eigenproblem parameter file '%s'", path);
fscanf(fp, "%lg", &par->prec);
fscanf(fp, "%d", &par->reortho);
fscanf(fp, "%d", &par->size);
fclose(fp);
}
}
void EigenParWrite(char *filename, struct EigenPar *par){
char path[MAX_FILE_NAME_LENGTH];
FILE *fp;
strcpy(path, Name_Path);
strcat(path, filename);
fp = fopen(path, "w+t");
if(fp){
fprintf(fp, "%g\n", par->prec);
fprintf(fp, "%d\n", par->reortho);
fprintf(fp, "%d\n", par->size);
fprintf(fp,
"/*\n"
" The numbers above are the parameters for the numerical\n"
" eigenvalue problem:\n"
"\n"
" prec = aimed accuracy for eigenvectors (default=1.e-4)\n"
" reortho = reorthogonalisation of Krylov basis: yes=1, no=0 (default=0) \n"
" size = size of the Krylov basis\n"
"\n"
" The shift is given in the .pro file because its choice relies\n"
" on physical considerations.\n"
"*/");
fclose(fp);
}
else{
Msg(ERROR, "unable to open file '%s'", path);
}
}
void EigenParPrint(struct EigenPar *par){
Msg(INFO, "Eigenproblem parameters: prec = %g, reortho = %d, size = %d",
par->prec, par->reortho, par->size);
}
/*
* Copyright (C) 1997-2005 P. Dular, C. Geuzaine
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Please report all bugs and problems to <getdp@geuz.org>.
*/
#ifndef _EIGEN_PAR_H_
#define _EIGEN_PAR_H_
struct EigenPar {
double prec;
int size;
int reortho;
} ;
void EigenParRead(char *filename, struct EigenPar *par);
void EigenParWrite(char *filename, struct EigenPar *par);
void EigenParPrint(struct EigenPar *par);
#endif
#define RCSID "$Id: Lanczos.c,v 1.26 2005-07-09 07:17:21 geuzaine Exp $"
#define RCSID "$Id: Lanczos.c,v 1.27 2005-07-11 15:00:17 geuzaine Exp $"
/*
* Copyright (C) 1997-2005 P. Dular, C. Geuzaine
*
......@@ -29,6 +29,7 @@
#include "DofData.h"
#include "CurrentData.h"
#include "Numeric.h"
#include "EigenPar.h"
/* Version commentee par A. Nicolet de Lanczos.c le 2001/11/29 */
......@@ -367,17 +368,7 @@ void Lanczos (struct DofData * DofData_P, int LanSize, List_T *LanSave, double s
long mun = -1 ;
#endif
struct Solution Solution_S ;
/* declaration pour les parametres de eigen.par */
struct eigenpro {
double prec;
int size; /* remplacer LanSize par eigenpar.size */
int reortho;
} eigenpar ;
char EigenFileName[MAX_FILE_NAME_LENGTH];
FILE * eigenf;
struct EigenPar eigenpar;
GetDP_Begin("Lanczos");
......@@ -389,44 +380,10 @@ void Lanczos (struct DofData * DofData_P, int LanSize, List_T *LanSave, double s
Msg(ERROR, "No System available for Lanczos: check 'DtDt' and 'GenerateSeparate'") ;
/* lecture des parametres dans le fichier 'eigen.par' */
eigenpar.prec = 1.e-4;
eigenpar.reortho = 0;
eigenpar.size = LanSize; /* FIXME: dans le .pro il faudrait changer
la syntaxe. Enlever LanSize et remplacer
le mot clef Lanczos par Eigenproblem */
strcpy(EigenFileName, Name_Path);
strcat(EigenFileName, "eigen.par");
Msg(INFO, "Loading eigenproblem parameter file '%s'", EigenFileName);
eigenf = fopen(EigenFileName, "r+t");
if (eigenf) { /* le fichier existe ! */
fscanf(eigenf, "%lg ", &eigenpar.prec);
Msg(INFO, "eigenpar.prec = %g", eigenpar.prec);
fscanf(eigenf, "%d ", &eigenpar.reortho);
Msg(INFO, "eigenpar.reortho = %d", eigenpar.reortho);
fscanf(eigenf, "%d ", &eigenpar.size);
Msg(INFO, "eigenpar.size = %d", eigenpar.size);
/* tester la fin du fichier avec un entier standard */
fclose(eigenf);
}
/* reecriture des parametres */
eigenf=fopen(EigenFileName, "w+t");
fprintf(eigenf, "%g \n", eigenpar.prec);
fprintf(eigenf, "%d \n", eigenpar.reortho);
fprintf(eigenf, "%d \n", eigenpar.size);
fprintf(eigenf,
"/*\n"
" The numbers above are the parameters for the numerical\n"
" eigenvalue problem:\n"
"\n"
" prec = aimed accuracy for eigenvectors (default=1.e-4)\n"
" reortho = reorthogonalisation of Krylov basis: yes=1, no=0 (default=0) \n"
" size = number of iterations and max size of the Krylov basis\n"
"\n"
" The shift is given in the .pro file because its choice relies\n"
" on physical considerations...\n"
"*/");
fclose(eigenf);
EigenParRead("eigen.par", &eigenpar);
eigenpar.size = LanSize; /* Hack: we force this */
EigenParWrite("eigen.par", &eigenpar);
EigenParPrint(&eigenpar);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
......
# $Id: Makefile,v 1.36 2005-07-08 21:54:52 geuzaine Exp $
# $Id: Makefile,v 1.37 2005-07-11 15:00:17 geuzaine Exp $
#
# Copyright (C) 1997-2005 P. Dular, C. Geuzaine
#
......@@ -28,6 +28,7 @@ CFLAGS = ${C_FLAGS} ${SOLVER} ${INCLUDE}
FFLAGS = ${F77_FLAGS} ${INCLUDE}
CSRC = Analytic_HollowCylLF.c \
EigenPar.c \
Lanczos.c \
Arpack.c \
Adapt.c \
......@@ -76,22 +77,28 @@ nodepend:
Analytic_HollowCylLF.o: Analytic_HollowCylLF.c ../include/GetDP.h \
../Main/Message.h ../DataStr/Malloc.h Numeric.h ../DataStr/List.h
# 1 "/Users/geuzaine/.getdp/Numeric//"
EigenPar.o: EigenPar.c ../include/GetDP.h ../Main/Message.h \
../DataStr/Malloc.h ../Main/CurrentData.h ../Main/Data_Active.h \
../Main/Data_Passive.h ../DataStr/List.h ../Main/Data_Element.h \
../GeoData/GeoData.h ../GeoData/Data_GeoData.h ../DataStr/Tree.h \
../DataStr/avl.h ../Main/Data_Element.h ../DofData/DofData.h \
../DofData/LinAlg.h ../Sparskit/Solver.h ../Main/Graph.h \
../DofData/Data_DofData.h ../include/Magic.h EigenPar.h
# 1 "/Users/geuzaine/.getdp/Numeric//"
Lanczos.o: Lanczos.c ../include/GetDP.h ../Main/Message.h \
../DataStr/Malloc.h ../DofData/DofData.h ../DataStr/List.h \
../DataStr/Tree.h ../DataStr/avl.h ../DofData/LinAlg.h \
../Sparskit/Solver.h ../Main/Graph.h ../DofData/Data_DofData.h \
../Main/CurrentData.h ../Main/Data_Active.h ../Main/Data_Passive.h \
../Main/Data_Element.h ../GeoData/GeoData.h ../GeoData/Data_GeoData.h \
../Main/Data_Element.h ../include/Magic.h Numeric.h ../include/Compat.h
../Main/Data_Element.h ../include/Magic.h Numeric.h EigenPar.h \
../include/Compat.h
# 1 "/Users/geuzaine/.getdp/Numeric//"
Arpack.o: Arpack.c ../include/GetDP.h ../Main/Message.h \
../DataStr/Malloc.h ../DofData/DofData.h ../DataStr/List.h \
../DataStr/Tree.h ../DataStr/avl.h ../DofData/LinAlg.h \
../Sparskit/Solver.h ../Main/Graph.h ../DofData/Data_DofData.h \
../Main/CurrentData.h ../Main/Data_Active.h ../Main/Data_Passive.h \
../Main/Data_Element.h ../GeoData/GeoData.h ../GeoData/Data_GeoData.h \
../Main/Data_Element.h ../include/Magic.h Numeric.h Arpack_F.h \
../include/Compat.h
Numeric.h EigenPar.h Arpack_F.h ../include/Compat.h
# 1 "/Users/geuzaine/.getdp/Numeric//"
Adapt.o: Adapt.c ../include/GetDP.h ../Main/Message.h ../DataStr/Malloc.h \
../Main/Data_Element.h ../GeoData/GeoData.h ../GeoData/Data_GeoData.h \
......
......@@ -14,6 +14,15 @@ environment variable) if the library is not installed in its default
location. Please note that compiling the Windows version requires the
Cygwin tools (freely available from http://www.cygwin.com).
By default, GetDP uses a built-in set of linear solvers derived from
Sparskit Version 2 (http://www-users.cs.umn.edu/~saad/). GetDP can
also be configured to use the linear solvers from PETSc 2.3 or higher
(freely available from http://www.mcs.anl.gov/petsc), using either
real or complex arithmetic. To configure GetDP with PETSc support,
type
./configure --enable-petsc
For a description of all other configuration options, type
./configure --help
......
# $Id: Makefile,v 1.23 2005-06-23 01:45:07 geuzaine Exp $
# $Id: Makefile,v 1.24 2005-07-11 15:00:17 geuzaine Exp $
#
# Copyright (C) 1997-2005 P. Dular, C. Geuzaine
#
......@@ -81,12 +81,16 @@ nodepend:
rm -f Makefile.new
# DO NOT DELETE THIS LINE
# 1 "/Users/geuzaine/.getdp/Sparskit//"
Solver.o: Solver.c Solver.h ../DataStr/List.h Solver_F.h \
../include/Compat.h ../Main/Message.h ../DataStr/Malloc.h \
../FMM/F_FMMOperations.h
# 1 "/Users/geuzaine/.getdp/Sparskit//"
Matrix.o: Matrix.c Solver.h ../DataStr/List.h Solver_F.h \
../include/Compat.h ../Main/Message.h ../DataStr/Malloc.h
# 1 "/Users/geuzaine/.getdp/Sparskit//"
Parser.o: Parser.c Solver.h ../DataStr/List.h ../Main/Message.h \
../DataStr/Malloc.h
# 1 "/Users/geuzaine/.getdp/Sparskit//"
CSR.o: CSR.c Solver.h ../DataStr/List.h ../Main/Message.h \
../DataStr/Malloc.h
$Id: CREDITS,v 1.13 2005-06-23 01:45:07 geuzaine Exp $
$Id: CREDITS,v 1.14 2005-07-11 15:00:17 geuzaine Exp $
GetDP is copyright (C) 1997-2005
......@@ -36,9 +36,14 @@ California makes no representations about the suitability of this
software for any purpose. It is provided "as is" without express or
implied warranty.
This version of GetDP may also contain code (in the Sparskit
subdirectory) copyright (C) 1990 Yousef Saad: check the configuration
options.
The Arpack code (in the Arpack subdirectory) was written by Danny
Sorensen, Richard Lehoucq, Chao Yang and Kristi Maschhoff from the
Dept. of Computational & Applied Mathematics at Rice University,
Houston, Texas, USA. See http://www.caam.rice.edu/software/ARPACK/ for
more info.
This version of GetDP may contain code (in the Sparskit subdirectory)
copyright (C) 1990 Yousef Saad: check the configuration options.
Thanks to the following folks who have contributed by providing fresh
ideas on theoretical or programming topics, who have sent patches,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment