DSDP
|
00001 #include "dsdpdatamat_impl.h" 00002 #include "dsdpsys.h" 00007 typedef struct { 00008 int neigs; 00009 double *eigval; 00010 double *an; 00011 int *cols,*nnz; 00012 } Eigen; 00013 00014 typedef struct { 00015 int nnzeros; 00016 const int *ind; 00017 const double *val; 00018 int ishift; 00019 double alpha; 00020 00021 Eigen *Eig; 00022 int factored; 00023 int owndata; 00024 int n; 00025 } vechmat; 00026 00027 #define GETI(a,b) (int)((int)a/(int)b) 00028 #define GETJ(a,b) (int)((int)a%(int)b) 00029 00030 static void getij(int k, int n, int *i,int *j){ 00031 *i=GETI(k,n); 00032 *j=GETJ(k,n); 00033 return; 00034 } 00035 00036 #undef __FUNCT__ 00037 #define __FUNCT__ "CreateVechMatWData" 00038 static int CreateVechMatWdata(int n, int ishift, double alpha, const int *ind, const double *vals, int nnz, vechmat **A){ 00039 int info; 00040 vechmat* V; 00041 DSDPCALLOC1(&V,vechmat,&info);DSDPCHKERR(info); 00042 V->n=n; V->ishift=ishift, V->ind=ind; V->val=vals;V->nnzeros=nnz; 00043 V->alpha=alpha; 00044 V->owndata=0; 00045 *A=V; 00046 return 0; 00047 } 00048 00049 static int VechMatAddMultiple(void* AA, double scl, double r[], int nn, int n){ 00050 vechmat* A=(vechmat*)AA; 00051 int k; 00052 const int *ind=A->ind,nnz=A->nnzeros; 00053 const double *val=A->val; 00054 double *rr=r-A->ishift; 00055 scl*=A->alpha; 00056 for (k=0; k<nnz; ++k){ 00057 *(rr+(ind[k])) +=scl*(val[k]); 00058 } 00059 return 0; 00060 } 00061 00062 static int VechMatDot(void* AA, double x[], int nn, int n, double *v){ 00063 vechmat* A=(vechmat*)AA; 00064 int k,nnz=A->nnzeros; 00065 const int *ind=A->ind; 00066 double vv=0,*xx=x-A->ishift; 00067 const double *val=A->val; 00068 for (k=0;k<nnz;++k,++ind,++val){ 00069 vv+=(*val)*(*(xx+(*ind))); 00070 } 00071 *v=2*vv*A->alpha; 00072 return 0; 00073 } 00074 00075 static int EigMatVecVec(Eigen*, double[], int, double*); 00076 static int VechMatGetRank(void*,int*,int); 00077 00078 static int VechMatVecVec(void* AA, double x[], int n, double *v){ 00079 vechmat* A=(vechmat*)AA; 00080 int info,rank=n,i=0,j,k,kk; 00081 const int *ind=A->ind,ishift=A->ishift,nnz=A->nnzeros; 00082 double vv=0,dd; 00083 const double *val=A->val; 00084 00085 if (A->factored==3){ 00086 info=VechMatGetRank(AA,&rank,n); 00087 if (nnz>3 && rank<nnz){ 00088 info=EigMatVecVec(A->Eig,x,n,&vv); 00089 *v=vv*A->alpha; 00090 return 0; 00091 } 00092 } 00093 00094 for (k=0; k<nnz; ++k,++ind,++val){ 00095 kk=*ind-ishift; 00096 i=GETI(kk,n); 00097 j=GETJ(kk,n); 00098 dd=x[i]*x[j]*(*val); 00099 vv+=2*dd; 00100 if (i==j){ vv-=dd; } 00101 } 00102 *v=vv*A->alpha; 00103 00104 return 0; 00105 } 00106 00107 00108 static int VechMatGetRowNnz(void* AA, int trow, int nz[], int *nnzz,int nn){ 00109 vechmat* A=(vechmat*)AA; 00110 int i=0,j,k,t; 00111 const int *ind=A->ind, ishift=A->ishift,nnz=A->nnzeros; 00112 *nnzz=0; 00113 for (k=0; k<nnz; ++k,ind){ 00114 t=ind[k]-ishift; 00115 i=GETI(t,nn); 00116 j=GETJ(t,nn); 00117 if (i==trow){ 00118 nz[j]++;(*nnzz)++; 00119 } else if (j==trow){ 00120 nz[i]++;(*nnzz)++; 00121 } 00122 } 00123 return 0; 00124 } 00125 00126 static int VechMatFNorm2(void* AA, int n, double *fnorm2){ 00127 vechmat* A=(vechmat*)AA; 00128 int i=0,j,k,t; 00129 const int *ind=A->ind,ishift=A->ishift,nnz=A->nnzeros; 00130 double fn2=0; 00131 const double *val=A->val; 00132 for (k=0; k<nnz; ++k){ 00133 t=ind[k]-ishift; 00134 i=GETI(t,n); 00135 j=GETJ(t,n); 00136 if (i==j){ 00137 fn2+= val[k]*val[k]; 00138 } else { 00139 fn2+= 2*val[k]*val[k]; 00140 } 00141 } 00142 *fnorm2=fn2*A->alpha*A->alpha; 00143 return 0; 00144 } 00145 00146 static int VechMatAddRowMultiple(void* AA, int trow, double scl, double r[], int m){ 00147 vechmat* A=(vechmat*)AA; 00148 int i=0,j,k,t,ishift=A->ishift,nnz=A->nnzeros; 00149 const int *ind=A->ind; 00150 const double *val=A->val; 00151 scl*=A->alpha; 00152 for (k=0; k<nnz; ++k){ 00153 t=ind[k]-ishift; 00154 i=GETI(t,m); 00155 j=GETJ(t,m); 00156 if (i==trow){ 00157 r[j]+=scl*val[k]; 00158 } else if (j==trow){ 00159 r[i]+=scl*val[k]; 00160 } 00161 } 00162 return 0; 00163 } 00164 00165 static int VechMatCountNonzeros(void* AA, int*nnz, int n){ 00166 vechmat* A=(vechmat*)AA; 00167 *nnz=A->nnzeros; 00168 return 0; 00169 } 00170 00171 #undef __FUNCT__ 00172 #define __FUNCT__ "VechMatDestroy" 00173 static int VechMatDestroy(void* AA){ 00174 vechmat* A=(vechmat*)AA; 00175 int info; 00176 if (A->owndata){ 00177 /* 00178 if (A->ind){ DSDPFREE(&A->ind,&info);DSDPCHKERR(info);} 00179 if (A->val){ DSDPFREE(&A->val,&info);DSDPCHKERR(info);} 00180 */ 00181 return 1; 00182 } 00183 if (A->Eig){ 00184 DSDPFREE(&A->Eig->eigval,&info);DSDPCHKERR(info); 00185 DSDPFREE(&A->Eig->an,&info);DSDPCHKERR(info); 00186 if (A->Eig->cols){DSDPFREE(&A->Eig->cols,&info);DSDPCHKERR(info);} 00187 if (A->Eig->nnz){DSDPFREE(&A->Eig->nnz,&info);DSDPCHKERR(info);} 00188 DSDPFREE(&A->Eig,&info);DSDPCHKERR(info); 00189 } 00190 DSDPFREE(&A,&info);DSDPCHKERR(info); 00191 return 0; 00192 } 00193 00194 00195 00196 #undef __FUNCT__ 00197 #define __FUNCT__ "DSDPCreateVechMatEigs" 00198 static int CreateEigenLocker(Eigen **EE,int iptr[], int neigs, int n){ 00199 int i,k,info; 00200 Eigen *E; 00201 00202 for (k=0,i=0;i<neigs;i++) k+=iptr[i]; 00203 if (k>n*neigs/4){ 00204 00205 DSDPCALLOC1(&E,Eigen,&info);DSDPCHKERR(info); 00206 DSDPCALLOC2(&E->eigval,double,neigs,&info);DSDPCHKERR(info); 00207 DSDPCALLOC2(&E->an,double,n*neigs,&info);DSDPCHKERR(info); 00208 E->neigs=neigs; 00209 E->cols=0; 00210 E->nnz=0; 00211 00212 } else { 00213 00214 DSDPCALLOC1(&E,Eigen,&info);DSDPCHKERR(info); 00215 DSDPCALLOC2(&E->eigval,double,neigs,&info);DSDPCHKERR(info); 00216 DSDPCALLOC2(&E->nnz,int,neigs,&info);DSDPCHKERR(info); 00217 DSDPCALLOC2(&E->an,double,k,&info);DSDPCHKERR(info); 00218 DSDPCALLOC2(&E->cols,int,k,&info);DSDPCHKERR(info); 00219 E->neigs=neigs; 00220 00221 if (neigs>0) E->nnz[0]=iptr[0]; 00222 for (i=1;i<neigs;i++){E->nnz[i]=E->nnz[i-1]+iptr[i];} 00223 } 00224 *EE=E; 00225 return 0; 00226 } 00227 00228 00229 static int EigMatSetEig(Eigen* A,int row, double eigv, int idxn[], double v[], int nsub,int n){ 00230 int j,k,*cols=A->cols; 00231 double *an=A->an; 00232 A->eigval[row]=eigv; 00233 if (cols){ 00234 k=0; if (row>0){ k=A->nnz[row-1];} 00235 cols+=k; an+=k; 00236 for (k=0,j=0; j<nsub; j++){ 00237 if (v[j]==0.0) continue; 00238 cols[k]=idxn[j]; an[k]=v[j]; k++; 00239 } 00240 } else { 00241 an+=n*row; 00242 for (j=0; j<nsub; j++){ 00243 if (v[j]==0.0) continue; 00244 an[idxn[j]]=v[j]; 00245 } 00246 } 00247 return 0; 00248 } 00249 00250 00251 static int EigMatGetEig(Eigen* A,int row, double *eigenvalue, double eigenvector[], int n, int spind[], int *nind){ 00252 int i,*cols=A->cols,bb,ee; 00253 double* an=A->an; 00254 *eigenvalue=A->eigval[row]; 00255 *nind=0; 00256 if (cols){ 00257 memset((void*)eigenvector,0,n*sizeof(double)); 00258 if (row==0){ bb=0;} else {bb=A->nnz[row-1];} ee=A->nnz[row]; 00259 for (i=bb;i<ee;i++){ 00260 eigenvector[cols[i]]=an[i]; 00261 spind[i-bb]=cols[i]; (*nind)++; 00262 } 00263 } else { 00264 memcpy((void*)eigenvector,(void*)(an+n*row),n*sizeof(double)); 00265 for (i=0;i<n;i++)spind[i]=i; 00266 *nind=n; 00267 } 00268 return 0; 00269 } 00270 00271 static int EigMatVecVec(Eigen* A, double v[], int n, double *vv){ 00272 int i,rank,*cols=A->cols,neigs=A->neigs,*nnz=A->nnz,bb,ee; 00273 double* an=A->an,*eigval=A->eigval,dd,ddd=0; 00274 00275 if (cols){ 00276 for (rank=0;rank<neigs;rank++){ 00277 if (rank==0){ bb=0;} else {bb=nnz[rank-1];} ee=nnz[rank]; 00278 for (dd=0,i=bb;i<ee;i++){ 00279 dd+=an[i]*v[cols[i]]; 00280 } 00281 ddd+=dd*dd*eigval[rank]; 00282 } 00283 } else { 00284 for (rank=0;rank<neigs;rank++){ 00285 for (dd=0,i=0;i<n;i++){ 00286 dd+=an[i]*v[i]; 00287 } 00288 an+=n; 00289 ddd+=dd*dd*eigval[rank]; 00290 } 00291 } 00292 *vv=ddd; 00293 return 0; 00294 } 00295 00296 00297 static int VechMatComputeEigs(vechmat*,double[],int,double[],int,double[],int,int[],int,double[],int,double[],int); 00298 00299 static int VechMatFactor(void*AA, double dmatp[], int nn0, double dwork[], int n, double ddwork[], int n1, int iptr[], int n2){ 00300 00301 vechmat* A=(vechmat*)AA; 00302 int i,j,k,t,info,isdiag; 00303 const int *ind=A->ind,ishift=A->ishift,nonzeros=A->nnzeros; 00304 double *ss1=0,*ss2=0; 00305 int nn1=0,nn2=0; 00306 if (A->factored) return 0; 00307 00308 memset((void*)iptr,0,3*n*sizeof(int)); 00309 /* Find number of nonzeros in each row */ 00310 for (isdiag=1,k=0; k<nonzeros; k++){ 00311 t=ind[k]-ishift; 00312 i=GETI(t,n); 00313 j=GETJ(t,n); 00314 iptr[i]++; 00315 if (i!=j) {isdiag=0;iptr[j]++;} 00316 } 00317 00318 if (isdiag){ A->factored=1; return 0;} 00319 /* Find most nonzeros per row */ 00320 for (j=0,i=0; i<n; i++){ if (iptr[i]>j) j=iptr[i]; } 00321 if (j<2){ A->factored=2; return 0; } 00322 00323 info=VechMatComputeEigs(A,dmatp,nn0,dwork,n,ddwork,n1,iptr,n2,ss1,nn1,ss2,nn2);DSDPCHKERR(info); 00324 A->factored=3; 00325 return 0; 00326 } 00327 00328 static int VechMatGetRank(void *AA,int *rank,int n){ 00329 vechmat* A=(vechmat*)AA; 00330 switch (A->factored){ 00331 case 1: 00332 *rank=A->nnzeros; 00333 break; 00334 case 2: 00335 *rank=2*A->nnzeros; 00336 break; 00337 case 3: 00338 *rank=A->Eig->neigs; 00339 break; 00340 default: 00341 DSDPSETERR(1,"Vech Matrix not factored yet\n"); 00342 } 00343 return 0; 00344 } 00345 00346 static int VechMatGetEig(void* AA, int rank, double *eigenvalue, double vv[], int n, int indx[], int *nind){ 00347 vechmat* A=(vechmat*)AA; 00348 const double *val=A->val,tt=sqrt(0.5); 00349 int info,i,j,k,t; 00350 const int *ind=A->ind,ishift=A->ishift; 00351 00352 *nind=0; 00353 switch (A->factored){ 00354 case 1: 00355 memset(vv,0,n*sizeof(double)); 00356 t=ind[rank]-ishift; 00357 i=GETI(t,n); 00358 j=GETJ(t,n); 00359 vv[i]=1.0; 00360 *eigenvalue=val[rank]*A->alpha; 00361 *nind=1; 00362 indx[0]=i; 00363 break; 00364 case 2: 00365 memset(vv,0,n*sizeof(double)); 00366 k=rank/2; 00367 getij(ind[k]-ishift,n,&i,&j); 00368 if (i==j){ 00369 if (k*2==rank){ 00370 vv[i]=1.0; *eigenvalue=val[k]*A->alpha; 00371 *nind=1; 00372 indx[0]=i; 00373 } else { 00374 *eigenvalue=0; 00375 } 00376 } else { 00377 if (k*2==rank){ 00378 vv[i]=tt; vv[j]=tt; *eigenvalue=val[k]*A->alpha; 00379 *nind=2; 00380 indx[0]=i; indx[1]=j; 00381 } else { 00382 vv[i]=-tt; vv[j]=tt; *eigenvalue=-val[k]*A->alpha; 00383 *nind=2; 00384 indx[0]=i; indx[1]=j; 00385 } 00386 } 00387 break; 00388 case 3: 00389 info=EigMatGetEig(A->Eig,rank,eigenvalue,vv,n,indx,nind);DSDPCHKERR(info); 00390 *eigenvalue=*eigenvalue*A->alpha; 00391 break; 00392 default: 00393 DSDPSETERR(1,"Vech Matrix not factored yet\n"); 00394 } 00395 00396 return 0; 00397 } 00398 00399 static int VechMatView(void* AA){ 00400 vechmat* A=(vechmat*)AA; 00401 int info,i=0,j,k,rank=0,ishift=A->ishift,n=A->n,nnz=A->nnzeros; 00402 const int *ind=A->ind; 00403 const double *val=A->val; 00404 for (k=0; k<nnz; k++){ 00405 getij(ind[k]-ishift,n,&i,&j); 00406 printf("Row: %d, Column: %d, Value: %10.8f \n",i,j,A->alpha*val[k]); 00407 } 00408 if (A->factored>0){ 00409 info=VechMatGetRank(AA,&rank,n);DSDPCHKERR(info); 00410 printf("Detected Rank: %d\n",rank); 00411 } 00412 return 0; 00413 } 00414 00415 00416 static struct DSDPDataMat_Ops vechmatops; 00417 static const char *datamatname="STANDARD VECH MATRIX"; 00418 00419 static int VechMatOpsInitialize(struct DSDPDataMat_Ops *sops){ 00420 int info; 00421 if (sops==NULL) return 0; 00422 info=DSDPDataMatOpsInitialize(sops); DSDPCHKERR(info); 00423 sops->matvecvec=VechMatVecVec; 00424 sops->matdot=VechMatDot; 00425 sops->matfnorm2=VechMatFNorm2; 00426 sops->mataddrowmultiple=VechMatAddRowMultiple; 00427 sops->mataddallmultiple=VechMatAddMultiple; 00428 sops->matview=VechMatView; 00429 sops->matdestroy=VechMatDestroy; 00430 sops->matfactor2=VechMatFactor; 00431 sops->matgetrank=VechMatGetRank; 00432 sops->matgeteig=VechMatGetEig; 00433 sops->matrownz=VechMatGetRowNnz; 00434 sops->matnnz=VechMatCountNonzeros; 00435 sops->id=3; 00436 sops->matname=datamatname; 00437 return 0; 00438 } 00439 00452 #undef __FUNCT__ 00453 #define __FUNCT__ "DSDPGetVecUMat" 00454 int DSDPGetVecUMat(int n,int ishift,double alpha,const int ind[], const double val[],int nnz, struct DSDPDataMat_Ops**sops, void**smat){ 00455 int info,i,j,k,itmp,nn=n*n; 00456 double dtmp; 00457 vechmat* AA; 00458 DSDPFunctionBegin; 00459 for (k=0;k<nnz;++k){ 00460 itmp=ind[k]-ishift; 00461 if (itmp>=nn){ 00462 getij(itmp,n,&i,&j); 00463 /* 00464 DSDPSETERR(2,"Illegal index value: Element %d in array has row %d (>0) or column %d (>0) is greater than %d. \n",k+1,i+1,j+1,n); 00465 */ 00466 DSDPSETERR3(2,"Illegal index value: Element %d in array has index %d greater than or equal to %d. \n",k,itmp,nn); 00467 } else if (itmp<0){ 00468 DSDPSETERR1(2,"Illegal index value: %d. Must be >= 0\n",itmp); 00469 } 00470 } 00471 for (k=0;k<nnz;++k) dtmp=val[k]; 00472 info=CreateVechMatWdata(n,ishift,alpha,ind,val,nnz,&AA); DSDPCHKERR(info); 00473 AA->factored=0; 00474 AA->Eig=0; 00475 info=VechMatOpsInitialize(&vechmatops); DSDPCHKERR(info); 00476 if (sops){*sops=&vechmatops;} 00477 if (smat){*smat=(void*)AA;} 00478 DSDPFunctionReturn(0); 00479 } 00480 00481 00482 #undef __FUNCT__ 00483 #define __FUNCT__ "VechMatComputeEigs" 00484 static int VechMatComputeEigs(vechmat* AA,double DD[], int nn0, double W[], int n, double WORK[], int n1, int iiptr[], int n2, double ss1[],int nn1, double ss2[], int nn2){ 00485 00486 int i,j,k,nsub,neigs,info,*iptr,*perm,*invp; 00487 long int *i2darray=(long int*)DD; 00488 int ownarray1=0,ownarray2=0,ownarray3=0; 00489 int ishift=AA->ishift,nonzeros=AA->nnzeros; 00490 const int *ind=AA->ind; 00491 const double *val=AA->val; 00492 double *dmatarray=ss1,*dworkarray=ss2,maxeig,eps=1.0e-12,eps2=1.0e-12; 00493 00494 iptr=iiptr; perm=iptr+n; invp=perm+n; 00495 /* These operations were done before calling this routine * / 00496 / * Integer arrays corresponding to rows with nonzeros and inverse map * / 00497 memset((void*)iiptr,0,3*n*sizeof(int)); 00498 00499 / * Find number of nonzeros in each row * / 00500 for (i=0,k=0; k<nonzeros; k++){ 00501 getij(ind[k],i,n,&i,&j); 00502 iptr[i]++; iptr[j]++; 00503 } 00504 */ 00505 /* Number of rows with a nonzero. Order the rows with nonzeros. */ 00506 for (nsub=0,i=0; i<n; i++){ 00507 if (iptr[i]>0){ invp[nsub]=i; perm[i]=nsub; nsub++;} 00508 } 00509 00510 /* create a dense array in which to put numbers */ 00511 if (nsub*nsub>nn1){ 00512 DSDPCALLOC2(&dmatarray,double,(nsub*nsub),&info); DSDPCHKERR(info); 00513 ownarray1=1; 00514 } 00515 memset((void*)dmatarray,0,nsub*nsub*sizeof(double)); 00516 if (nsub*nsub>nn2){ 00517 DSDPCALLOC2(&dworkarray,double,(nsub*nsub),&info); DSDPCHKERR(info); 00518 ownarray2=1; 00519 } 00520 00521 if (nsub*nsub*sizeof(long int)>nn0*sizeof(double)){ 00522 DSDPCALLOC2(&i2darray,long int,(nsub*nsub),&info); DSDPCHKERR(info); 00523 ownarray3=1; 00524 } 00525 00526 00527 for (i=0,k=0; k<nonzeros; k++){ 00528 getij(ind[k]-ishift,n,&i,&j); 00529 dmatarray[perm[i]*nsub+perm[j]] += val[k]; 00530 if (i!=j){ 00531 dmatarray[perm[j]*nsub+perm[i]] += val[k]; 00532 } 00533 } 00534 /* Call LAPACK to compute the eigenvalues */ 00535 memset((void*)W,0,n*sizeof(double)); 00536 00537 info=DSDPGetEigs(dmatarray,nsub,dworkarray,nsub*nsub,i2darray,nsub*nsub, 00538 W,nsub,WORK,n1,iiptr+3*n,n2-3*n); 00539 if (info){ 00540 memset((void*)dmatarray,0,nsub*nsub*sizeof(double)); 00541 for (i=0,k=0; k<nonzeros; k++){ 00542 getij(ind[k]-ishift,n,&i,&j); 00543 dmatarray[perm[i]*nsub+perm[j]] += val[k]; 00544 if (i!=j){ 00545 dmatarray[perm[j]*nsub+perm[i]] += val[k]; 00546 } 00547 } 00548 info=DSDPGetEigs2(dmatarray,nsub,dworkarray,nsub*nsub,i2darray,nsub*nsub, 00549 W,nsub,WORK,n1,iiptr+3*n,n2-3*n); DSDPCHKERR(info); 00550 } 00551 /* dsyev_("V","L",&N,dmatarray,&LDA,W,WORK,&LWORK,&INFO); */ 00552 00553 for (maxeig=0,i=0;i<nsub;i++){ 00554 if (fabs(W[i])>maxeig){ maxeig=fabs(W[i]); } 00555 } 00556 memset((void*)iptr,0,nsub*sizeof(int)); 00557 /* Compute sparsity pattern for eigenvalue and eigenvector structures */ 00558 /* Count the nonzero eigenvalues */ 00559 for (neigs=0,k=0; k<nsub; k++){ 00560 if (fabs(W[k]) /* /maxeig */ > eps){ 00561 for (j=0;j<nsub;j++){ 00562 if (fabs(dmatarray[nsub*k+j]) >= eps2){iptr[neigs]++; 00563 } else { dmatarray[nsub*k+j]=0.0;} 00564 } 00565 neigs++; 00566 /* 00567 } else if (fabs(W[k])>1.0e-100){ 00568 printf("SKIPPING EIGENVALUE: %4.4e, max is : %4.4e\n",W[k],maxeig); 00569 */ 00570 } 00571 } 00572 00573 info=CreateEigenLocker(&AA->Eig,iptr,neigs,n);DSDPCHKERR(info); 00574 DSDPLogInfo(0,49," Data Mat has %d eigenvectors.",neigs); 00575 /* Copy into structure */ 00576 for (neigs=0,i=0; i<nsub; i++){ 00577 if (fabs(W[i]) > eps){ 00578 info=EigMatSetEig(AA->Eig,neigs,W[i],invp,dmatarray+nsub*i,nsub,n);DSDPCHKERR(info); 00579 neigs++; 00580 } 00581 } 00582 00583 if (ownarray1){ DSDPFREE(&dmatarray,&info);DSDPCHKERR(info);} 00584 if (ownarray2){ DSDPFREE(&dworkarray,&info);DSDPCHKERR(info);} 00585 if (ownarray3){ DSDPFREE(&i2darray,&info);DSDPCHKERR(info);} 00586 return 0; 00587 } 00588