plapack.h
Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00043 
#ifndef plapack_h
00044 
#define plapack_h
00045 
00046 
#include "Mat.h"
00047 
#include "TMat_maths.h"
00048 
00049 
#include "lapack_proto.h"
00050 
00051 
namespace PLearn {
00052 
using namespace std;
00053 
00054 
00055 inline void lapack_Xsyevx_(
char* JOBZ, 
char* RANGE, 
char* UPLO, 
int* N, 
double* A, 
int* LDA, 
double* VL, 
double* VU, 
int* IL, 
int* IU, 
double* ABSTOL, 
int* M, 
double* W, 
double* Z, 
int* LDZ, 
double* WORK, 
int* LWORK, 
int* IWORK, 
int* IFAIL, 
int* INFO)
00056 { 
dsyevx_(JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU, ABSTOL, M, W, Z, LDZ, WORK, LWORK, IWORK, IFAIL, INFO); }
00057 
00058 inline void lapack_Xsyevx_(
char* JOBZ, 
char* RANGE, 
char* UPLO, 
int* N, 
float* A, 
int* LDA, 
float* VL, 
float* VU, 
int* IL, 
int* IU, 
float* ABSTOL, 
int* M, 
float* W, 
float* Z, 
int* LDZ, 
float* WORK, 
int* LWORK, 
int* IWORK, 
int* IFAIL, 
int* INFO)
00059 { 
ssyevx_(JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU, ABSTOL, M, W, Z, LDZ, WORK, LWORK, IWORK, IFAIL, INFO); }
00060 
00061 inline void lapack_Xgesdd_(
char* JOBZ, 
int* M, 
int* N, 
double* A, 
int* LDA, 
double* S, 
double* U, 
int* LDU, 
double* VT, 
int* LDVT, 
double* WORK, 
int* LWORK, 
int* IWORK, 
int* INFO)
00062 { 
dgesdd_(JOBZ, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, LWORK, IWORK, INFO); }
00063 
00064 inline void lapack_Xgesdd_(
char* JOBZ, 
int* M, 
int* N, 
float* A, 
int* LDA, 
float* S, 
float* U, 
int* LDU, 
float* VT, 
int* LDVT, 
float* WORK, 
int* LWORK, 
int* IWORK, 
int* INFO)
00065 { 
sgesdd_(JOBZ, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, LWORK, IWORK, INFO); }
00066 
00067 inline void lapack_Xsyevr_(
char* JOBZ, 
char* RANGE, 
char* UPLO, 
int* N, 
float* A, 
int* LDA, 
float* VL, 
float* VU, 
int* IL, 
int* IU, 
float* ABSTOL, 
int* M, 
float* W, 
float* Z, 
int* LDZ, 
int* ISUPPZ, 
float* WORK, 
int* LWORK, 
int* IWORK, 
int* LIWORK, 
int* INFO)
00068 { 
ssyevr_(JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU, ABSTOL, M, W, Z, LDZ, ISUPPZ, WORK, LWORK, IWORK, LIWORK, INFO);}
00069 
00070 inline void lapack_Xsyevr_(
char* JOBZ, 
char* RANGE, 
char* UPLO, 
int* N, 
double* A, 
int* LDA, 
double* VL, 
double* VU, 
int* IL, 
int* IU, 
double* ABSTOL, 
int* M, 
double* W, 
double* Z, 
int* LDZ, 
int* ISUPPZ, 
double* WORK, 
int* LWORK, 
int* IWORK, 
int* LIWORK, 
int* INFO)
00071 { 
dsyevr_(JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU, ABSTOL, M, W, Z, LDZ, ISUPPZ, WORK, LWORK, IWORK, LIWORK, INFO);}
00072 
00073 inline void lapack_Xsygvx_(
int* ITYPE, 
char* JOBZ, 
char* RANGE, 
char* UPLO, 
int* N, 
double* A, 
int* LDA, 
double* B, 
int* LDB, 
double* VL, 
double* VU, 
int* IL, 
int* IU, 
double* ABSTOL, 
int* M, 
double* W, 
double* Z, 
int* LDZ, 
double* WORK, 
int* LWORK, 
int* IWORK, 
int* IFAIL, 
int* INFO)
00074 { 
dsygvx_(ITYPE, JOBZ, RANGE, UPLO, N, A, LDA, B, LDB, VL, VU, IL, IU, ABSTOL, M, W, Z, LDZ, WORK, LWORK, IWORK, IFAIL, INFO); }
00075 
00076 inline void lapack_Xsygvx_(
int* ITYPE, 
char* JOBZ, 
char* RANGE, 
char* UPLO, 
int* N, 
float* A, 
int* LDA, 
float* B, 
int* LDB, 
float* VL, 
float* VU, 
int* IL, 
int* IU, 
float* ABSTOL, 
int* M, 
float* W, 
float* Z, 
int* LDZ, 
float* WORK, 
int* LWORK, 
int* IWORK, 
int* IFAIL, 
int* INFO)
00077 { 
ssygvx_(ITYPE, JOBZ, RANGE, UPLO, N, A, LDA, B, LDB, VL, VU, IL, IU, ABSTOL, M, W, Z, LDZ, WORK, LWORK, IWORK, IFAIL, INFO); }
00078 
00079 
00083 
00099 
00100 
template<
class num_t>
00101 void lapackEIGEN(
const TMat<num_t>& A, 
TVec<num_t>& eigenvals, 
TMat<num_t>& eigenvecs, 
char RANGE=
'A', num_t low=0, num_t high=0, num_t ABSTOL=0)
00102 {
00103 
00104 
#ifdef BOUNDCHECK
00105 
  if(A.
length()!=A.
width())
00106     
PLERROR(
"In lapackEIGEN length and width of A differ, it should be symmetric!");
00107 
#endif
00108 
00109   
char JOBZ = eigenvecs.
isEmpty() ?
'N' :
'V';
00110   
char UPLO = 
'U';  
00111   
int N = A.
length();
00112   
int LDA = A.
mod();
00113 
00114   
int IL=0, IU=0;
00115   num_t VL=0, VU=0;
00116 
00117   eigenvals.
resize(N);
00118   
int M; 
00119 
00120   
switch(RANGE)
00121     {
00122     
case 'A':
00123       
if(JOBZ==
'V')
00124         eigenvecs.
resize(N, N);
00125       
break;
00126     
case 'I': 
00127       IL = 
int(low)+1; 
00128       IU = int(high)+1;
00129       
if(JOBZ==
'V')
00130         eigenvecs.
resize(IU-IL+1, N);
00131       
break;
00132     
case 'V':
00133       VL = low;
00134       VU = high;
00135       
if(JOBZ==
'V')
00136         eigenvecs.
resize(N, N);
00137       
break;
00138     
default:
00139       
PLERROR(
"In lapackEIGEN: invalid RANGE character: %c",RANGE);
00140     }
00141   
00142   num_t* Z = 0;
00143   
int LDZ = 1;
00144   
if(eigenvecs.
isNotEmpty())
00145     {
00146       Z = eigenvecs.
data();
00147       LDZ = eigenvecs.
mod();
00148     }
00149 
00150   
00151   
static TVec<num_t> WORK;
00152   
static TVec<int> IWORK;
00153   
static TVec<int> IFAIL;
00154 
00155   WORK.
resize(1);
00156   IWORK.
resize(5*N);
00157   IFAIL.
resize(N);
00158 
00159   
int LWORK = -1;
00160   
int INFO;
00161 
00162 
00163   
00164   
00165   
lapack_Xsyevx_( &JOBZ, &RANGE, &UPLO, &N, A.
data(), &LDA,  &VL,  &VU,
00166            &IL,  &IU,  &ABSTOL,  &M,  eigenvals.
data(), Z, &LDZ, 
00167            WORK.
data(), &LWORK, IWORK.
data(), IFAIL.
data(), &INFO );
00168   
00169 
00170   
if(INFO!=0)
00171     
PLERROR(
"In lapackEIGEN, problem in first call to sgesdd_ to get optimal work size, returned INFO = %d",INFO); 
00172   
00173   
00174   LWORK = (
int) WORK[0]; 
00175   WORK.
resize(LWORK);
00176 
00177   
00178   
00179   
lapack_Xsyevx_( &JOBZ, &RANGE, &UPLO, &N, A.
data(), &LDA,  &VL,  &VU,
00180            &IL,  &IU,  &ABSTOL,  &M,  eigenvals.
data(), Z, &LDZ, 
00181            WORK.
data(), &LWORK, IWORK.
data(), IFAIL.
data(), &INFO );
00182   
00183 
00184   
if(INFO!=0)
00185     
PLERROR(
"In lapackEIGEN, problem when calling sgesdd_ to perform computation, returned INFO = %d",INFO); 
00186 
00187   eigenvals.
resize(M);
00188   
if(JOBZ==
'V')
00189     eigenvecs.
resize(M, N);
00190 }
00191 
00198 
00220 
00221 
template<
class num_t>
00222 void lapackGeneralizedEIGEN(
const TMat<num_t>& A, 
const TMat<num_t>& B, 
int ITYPE, 
TVec<num_t>& eigenvals, 
TMat<num_t>& eigenvecs, 
char RANGE=
'A', num_t low=0, num_t high=0, num_t ABSTOL=0)
00223 {
00224 
00225 
#ifdef BOUNDCHECK
00226 
  if(A.
length()!=A.
width())
00227     
PLERROR(
"In lapackGeneralizedEIGEN length and width of A differ, it should be symmetric!");
00228 
#endif
00229 
00230   
char JOBZ = eigenvecs.
isEmpty() ?
'N' :
'V';
00231   
char UPLO = 
'U';  
00232   
int N = A.
length();
00233   
int LDA = A.
mod();
00234   
int LDB = B.
mod();
00235 
00236   
int IL=0, IU=0;
00237   num_t VL=0, VU=0;
00238 
00239   eigenvals.
resize(N);
00240   
int M; 
00241 
00242   
switch(RANGE)
00243     {
00244     
case 'A':
00245       
if(JOBZ==
'V')
00246         eigenvecs.
resize(N, N);
00247       
break;
00248     
case 'I': 
00249       IL = 
int(low)+1; 
00250       IU = int(high)+1;
00251       
if(JOBZ==
'V')
00252         eigenvecs.
resize(IU-IL+1, N);
00253       
break;
00254     
case 'V':
00255       VL = low;
00256       VU = high;
00257       
if(JOBZ==
'V')
00258         eigenvecs.
resize(N, N);
00259       
break;
00260     
default:
00261       
PLERROR(
"In lapackGeneralizedEIGEN: invalid RANGE character: %c",RANGE);
00262     }
00263   
00264   num_t* Z = 0;
00265   
int LDZ = 1;
00266   
if(eigenvecs.
isNotEmpty())
00267     {
00268       Z = eigenvecs.
data();
00269       LDZ = eigenvecs.
mod();
00270     }
00271 
00272   
00273   
static TVec<num_t> WORK;
00274   
static TVec<int> IWORK;
00275   
static TVec<int> IFAIL;
00276 
00277   WORK.
resize(1);
00278   IWORK.
resize(5*N);
00279   IFAIL.
resize(N);
00280 
00281   
int LWORK = -1;
00282   
int INFO;
00283 
00284 
00285   
00286   
00287   
lapack_Xsygvx_( &ITYPE, &JOBZ, &RANGE, &UPLO, &N, A.
data(), &LDA, B.
data(), &LDB,  &VL,  &VU,
00288            &IL,  &IU,  &ABSTOL,  &M,  eigenvals.
data(), Z, &LDZ, 
00289            WORK.
data(), &LWORK, IWORK.
data(), IFAIL.
data(), &INFO );
00290   
00291 
00292   
if(INFO!=0)
00293     
PLERROR(
"In lapackGeneralizedEIGEN, problem in first call to sgesdd_ to get optimal work size, returned INFO = %d",INFO); 
00294   
00295   
00296   LWORK = (
int) WORK[0]; 
00297   WORK.
resize(LWORK);
00298 
00299   
00300   
00301   
lapack_Xsygvx_( &ITYPE, &JOBZ, &RANGE, &UPLO, &N, A.
data(), &LDA, B.
data(), &LDB,  &VL,  &VU,
00302            &IL,  &IU,  &ABSTOL,  &M,  eigenvals.
data(), Z, &LDZ, 
00303            WORK.
data(), &LWORK, IWORK.
data(), IFAIL.
data(), &INFO );
00304   
00305 
00306   
if(INFO!=0)
00307     
PLERROR(
"In lapackGeneralizedEIGEN, problem when calling sgesdd_ to perform computation, returned INFO = %d",INFO); 
00308 
00309   eigenvals.
resize(M);
00310   
if(JOBZ==
'V')
00311     eigenvecs.
resize(M, N);
00312 }
00313 
00319 
00320 
00321 
template<
class num_t>
00322 void eigenVecOfSymmMat(
TMat<num_t>& m, 
int k, 
TVec<num_t>& eigen_values, 
TMat<num_t>& eigen_vectors)
00323 {
00324   eigen_vectors.
resize(
k,m.
width());
00325   eigen_values.
resize(
k);
00326   
00327   
if(
k>= m.
width())
00328     
lapackEIGEN(m, eigen_values, eigen_vectors, 
'A',num_t(0),num_t(0));
00329   
else
00330     
lapackEIGEN(m, eigen_values, eigen_vectors, 
'I', num_t(m.
width()-
k), num_t(m.
width()-1));
00331 
00332   
00333   eigen_values.
swap();
00334   eigen_vectors.
swapUpsideDown();
00335 }
00336 
00347 
00348 
00349 
template<
class num_t>
00350 void generalizedEigenVecOfSymmMat(
TMat<num_t>& m1, 
TMat<num_t>& m2, 
int itype, 
int k, 
TVec<num_t>& eigen_values, 
TMat<num_t>& eigen_vectors)
00351 {
00352   
if(m1.
length() != m2.
length() || m1.
width() != m2.
width())
00353     
PLERROR(
"In generalizedEigenVecOfSymmMat, m1 and m2 must have the same size"); 
00354 
00355   eigen_vectors.
resize(
k,m1.
width());
00356   eigen_values.
resize(
k);
00357   
00358   
if(
k>= m1.
width())
00359     
lapackGeneralizedEIGEN(m1, m2, itype, eigen_values, eigen_vectors, 
'A',num_t(0),num_t(0));
00360   
else
00361     
lapackGeneralizedEIGEN(m1, m2, itype, eigen_values, eigen_vectors, 
'I', num_t(m1.
width()-
k), num_t(m1.
width()-1));
00362 
00363   
00364   eigen_values.
swap();
00365   eigen_vectors.
swapUpsideDown();
00366 }
00367 
00368 
00369 
00384 
00385 
template<
class num_t>
00386 void lapackSVD(
const TMat<num_t>& At, 
TMat<num_t>& Ut, 
TVec<num_t>& S, 
TMat<num_t>& V, 
char JOBZ=
'A', 
real safeguard = 1)
00387 {            
00388   
int M = At.
width();
00389   
int N = At.
length();
00390   
int LDA = At.
mod();
00391   
int min_M_N = 
min(M,N);
00392   S.
resize(min_M_N);
00393 
00394   
switch(JOBZ)
00395     {
00396     
case 'A':
00397       Ut.
resize(M,M);
00398       V.
resize(N,N);
00399       
break;
00400     
case 'S':
00401       Ut.
resize(min_M_N, M);
00402       V.
resize(N, min_M_N);
00403       
break;
00404     
case 'O':
00405       
if(M<N)
00406         Ut.
resize(M,M); 
00407       
else
00408         V.
resize(N,N); 
00409       
break;
00410     
case 'N':
00411       
break;
00412     
default:
00413       
PLERROR(
"In lapackSVD, bad JOBZ argument : %c",JOBZ);
00414     }
00415 
00416 
00417   
int LDU = 1;
00418   
int LDVT = 1;
00419   num_t* U = 0;
00420   num_t* VT = 0;
00421 
00422   
if(V.
isNotEmpty())
00423     {
00424       LDVT = V.
mod();
00425       VT = V.
data();
00426     }
00427   
if(Ut.
isNotEmpty())
00428     {
00429       LDU = Ut.
mod();
00430       U = Ut.
data();
00431     }
00432 
00433   
static TVec<num_t> WORK;
00434   WORK.
resize(1);
00435   
int LWORK = -1;
00436 
00437   
static TVec<int> IWORK;
00438   IWORK.
resize(8*min_M_N);
00439 
00440   
int INFO;
00441 
00442   
00443   
lapack_Xgesdd_(&JOBZ, &M, &N, At.
data(), &LDA, S.
data(), U, &LDU, VT, &LDVT, WORK.
data(), &LWORK, IWORK.
data(), &INFO);
00444 
00445   
if(INFO!=0)
00446     
PLERROR(
"In lapackSVD, problem in first call to sgesdd_ to get optimal work size, returned INFO = %d",INFO); 
00447   
00448   
00449   LWORK = 
int(WORK[0] * safeguard + 0.5); 
00450   WORK.
resize(LWORK);
00451   
00452 
00453   
00454   
lapack_Xgesdd_(&JOBZ, &M, &N, At.
data(), &LDA, S.
data(), U, &LDU, VT, &LDVT, WORK.
data(), &LWORK, IWORK.
data(), &INFO );
00455 
00456   
if(INFO!=0)
00457     {      
00458       cerr << At << 
endl;
00459       
PLERROR(
"In lapackSVD, problem when calling sgesdd_ to perform computation, returned INFO = %d",INFO); 
00460     }
00461 }
00462 
00465 
00499 
00500 
template<
class num_t>
00501 inline void SVD(
const TMat<num_t>& A, 
TMat<num_t>& U, 
TVec<num_t>& S, 
TMat<num_t>& Vt, 
char JOBZ=
'A', 
real safeguard = 1)
00502 {
00503   
00504   
lapackSVD(A,Vt,S,U,JOBZ, safeguard);
00505 }
00506 
00507 
00508 
00509 
00526 
int eigen_SymmMat(Mat& in, Vec& e_value, Mat& e_vector, 
int& n_evalues_found, 
00527                   
bool compute_all, 
int nb_eigen, 
bool compute_vectors = 
true,
00528                   
bool largest_evalues=
true);
00529 
00530 
00531 
00533 
int eigen_SymmMat_decreasing(Mat& in, Vec& e_value, Mat& e_vector, 
int& n_evalues_found, 
00534                   
bool compute_all, 
int nb_eigen, 
bool compute_vectors = 
true,
00535                   
bool largest_evalues=
true);
00536 
00539 
int matInvert(Mat& in, Mat& inverse);
00540 
00543 Mat 
multivariate_normal(
const Vec& mu, 
const Mat& A, 
int N);
00544 
00547 Vec 
multivariate_normal(
const Vec& mu, 
const Mat& A);
00548 
00551 Vec 
multivariate_normal(
const Vec& mu, 
const Vec& e_values, 
const Mat& e_vectors);
00552 
00557 
void multivariate_normal(Vec& x, 
const Vec& mu, 
const Vec& e_values, 
const Mat& e_vectors, Vec& z);
00558 
00571 
int lapackSolveLinearSystem(Mat& At, Mat& Bt, TVec<int>& pivots);
00572 
00578 Mat 
solveLinearSystem(
const Mat& A, 
const Mat& B);
00579 
00582 Vec 
solveLinearSystem(
const Mat& A, 
const Vec& b);
00583 
00586 
void solveLinearSystem(
const Mat& A, 
const Mat& Y, Mat& X);
00587 
00590 
void solveTransposeLinearSystem(
const Mat& A, 
const Mat& Y, Mat& X);
00591 
00597 Vec 
constrainedLinearRegression(
const Mat& Xt, 
const Vec& Y, 
real lambda=0.);
00598 
00599 
00600 
00601 
00602 
00603 
00604 
00605 
00606 
00607 
00608 
00609 
00610 
00611 
00612 
void affineNormalization(Mat data, Mat W, Vec bias, 
real regularizer=0);
00613 
00615 inline Vec closestPointOnHyperplane(
const Vec& x, 
const Mat& points, 
real weight_decay = 0.)
00616 { 
return transposeProduct(points, 
constrainedLinearRegression(points,
x,weight_decay)); }
00617 
00619 inline real hyperplaneDistance(
const Vec& x, 
const Mat& points, 
real weight_decay = 0.)
00620 { 
return L2distance(
x, 
closestPointOnHyperplane(
x,points,weight_decay)); }
00621 
00640 
template<
class MatT>
00641 void diagonalizeSubspace(MatT& A, 
Mat& X, 
Vec& Ax, 
00642                          
Mat& solutions, 
Vec& evalues, 
Mat& evectors)
00643 {
00644   
int n_try=X.
length();
00645 
00646   n_try=
GramSchmidtOrthogonalization(X);
00647   X = X.
subMatRows(0,n_try);
00648 
00649   
int n_soln=solutions.
length();
00651   
Mat C(n_try,n_try);
00652   
for (
int i=0;i<n_try;i++)
00653   {
00654     
real* Ci = C[i];
00655     
Vec x_i=X(i);
00656     A.product(x_i,Ax);
00657     
for (
int j=0;j<=i;j++)
00658       Ci[j] = 
dot(X(j),Ax);
00659   }
00661   
for (
int i=0;i<n_try;i++)
00662   {
00663     
real* Ci = C[i];
00664     
for (
int j=i+1;j<n_try;j++)
00665       Ci[j] = C(j,i);
00666   }
00667 
00669   
int n_evalues_found=0;
00670   
Mat CC=C.
copy();
00671   
eigen_SymmMat(CC,evalues,evectors,n_evalues_found,
true,n_try,
true,
true);
00673 
#if 0
00674 
00675   
Vec Cv(n_try);
00676   
for (
int i=0;i<n_try;i++)
00677   {
00678     
Vec vi=evectors(i);
00679     
if (fabs(
norm(vi)-1)>1e-5)
00680     cout << 
"norm v[" << i << 
"] = " << 
norm(vi) << 
endl;
00681     
product(C, vi,Cv);
00682     
real ncv=
norm(Cv);
00683     
if (fabs(ncv-evalues[i])>1e-5)
00684     cout << 
"C v[" << i << 
"] = " << ncv << 
" but evalue = " << evalues[i] << 
endl;
00685     
real vcv = 
dot(vi,Cv);
00686     
if (fabs(vcv-evalues[i])>1e-5)
00687       cout << 
"v' C v[" << i << 
"] = " << vcv << 
" but evalue = " << evalues[i] << 
endl;
00688     
for (
int j=0;j<i;j++)
00689     {
00690       
Vec vj=evectors(j);
00691       
real dij = 
dot(vi,vj);
00692       
if (fabs(dij)>1e-5)
00693         cout << 
"v[" << i << 
"] . v[" << j << 
"] = " << dij << 
endl;
00694     }
00695   }
00696 
#endif
00697 
00699   
for (
int i=0;i<n_soln;i++)
00700   {
00701     
Vec xi=solutions(i);
00702     xi.
clear(); 
00703     
for (
int j=0;j<n_try;j++)
00704       
multiplyAcc(xi, X(j),evectors(i,j));
00705   }
00706 
#if 0
00707 
00708   
for (
int i=0;i<n_soln;i++)
00709   {
00710     
Vec xi=solutions(i);
00711     
real normxi=
norm(xi);
00712     
if (fabs(normxi-1)>1e-5)
00713       cout << 
"norm x[" << i << 
"]=" << normxi << 
endl;
00714     
product(A, xi,Ax); 
00715     cout << 
"Ax[" << i << 
"]=" << 
norm(Ax) << 
endl;
00716     
real xAx = 
dot(Ax,xi);
00717     
real err=fabs(xAx-evalues[i]);
00718     
if (err>1e-5)
00719       cout << 
"xAx [" << i << 
"]=" << xAx << 
" but evalue = " << evalues[i] << 
endl;
00720     
for (
int j=0;j<i;j++)
00721     {
00722       
Vec xj=solutions(j);
00723       err = fabs(
dot(xi,xj));
00724       
if (err>1e-5)
00725         cout << 
"|x[" << i << 
"] . x[" << j << 
"]| = " << err << 
endl;
00726     }
00727   }
00728 
#endif
00729 
}
00730 
00731 } 
00732 
00733 
00734 
#endif 
00735 
Generated on Tue Aug 17 16:01:26 2004 for PLearn by
 1.3.7