|
Graphviz
2.29.20120524.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 00015 #include "matrix_ops.h" 00016 #include "memory.h" 00017 #include <stdlib.h> 00018 #include <stdio.h> 00019 #include <math.h> 00020 00021 static double p_iteration_threshold = 1e-3; 00022 00023 int 00024 power_iteration(double **square_mat, int n, int neigs, double **eigs, 00025 double *evals, int initialize) 00026 { 00027 /* compute the 'neigs' top eigenvectors of 'square_mat' using power iteration */ 00028 00029 int i, j; 00030 double *tmp_vec = N_GNEW(n, double); 00031 double *last_vec = N_GNEW(n, double); 00032 double *curr_vector; 00033 double len; 00034 double angle; 00035 double alpha; 00036 int iteration = 0; 00037 int largest_index; 00038 double largest_eval; 00039 int Max_iterations = 30 * n; 00040 00041 double tol = 1 - p_iteration_threshold; 00042 00043 if (neigs >= n) { 00044 neigs = n; 00045 } 00046 00047 for (i = 0; i < neigs; i++) { 00048 curr_vector = eigs[i]; 00049 /* guess the i-th eigen vector */ 00050 choose: 00051 if (initialize) 00052 for (j = 0; j < n; j++) 00053 curr_vector[j] = rand() % 100; 00054 /* orthogonalize against higher eigenvectors */ 00055 for (j = 0; j < i; j++) { 00056 alpha = -dot(eigs[j], 0, n - 1, curr_vector); 00057 scadd(curr_vector, 0, n - 1, alpha, eigs[j]); 00058 } 00059 len = norm(curr_vector, 0, n - 1); 00060 if (len < 1e-10) { 00061 /* We have chosen a vector colinear with prvious ones */ 00062 goto choose; 00063 } 00064 vecscale(curr_vector, 0, n - 1, 1.0 / len, curr_vector); 00065 iteration = 0; 00066 do { 00067 iteration++; 00068 cpvec(last_vec, 0, n - 1, curr_vector); 00069 00070 right_mult_with_vector_d(square_mat, n, n, curr_vector, 00071 tmp_vec); 00072 cpvec(curr_vector, 0, n - 1, tmp_vec); 00073 00074 /* orthogonalize against higher eigenvectors */ 00075 for (j = 0; j < i; j++) { 00076 alpha = -dot(eigs[j], 0, n - 1, curr_vector); 00077 scadd(curr_vector, 0, n - 1, alpha, eigs[j]); 00078 } 00079 len = norm(curr_vector, 0, n - 1); 00080 if (len < 1e-10 || iteration > Max_iterations) { 00081 /* We have reached the null space (e.vec. associated with e.val. 0) */ 00082 goto exit; 00083 } 00084 00085 vecscale(curr_vector, 0, n - 1, 1.0 / len, curr_vector); 00086 angle = dot(curr_vector, 0, n - 1, last_vec); 00087 } while (fabs(angle) < tol); 00088 evals[i] = angle * len; /* this is the Rayleigh quotient (up to errors due to orthogonalization): 00089 u*(A*u)/||A*u||)*||A*u||, where u=last_vec, and ||u||=1 00090 */ 00091 } 00092 exit: 00093 for (; i < neigs; i++) { 00094 /* compute the smallest eigenvector, which are */ 00095 /* probably associated with eigenvalue 0 and for */ 00096 /* which power-iteration is dangerous */ 00097 curr_vector = eigs[i]; 00098 /* guess the i-th eigen vector */ 00099 for (j = 0; j < n; j++) 00100 curr_vector[j] = rand() % 100; 00101 /* orthogonalize against higher eigenvectors */ 00102 for (j = 0; j < i; j++) { 00103 alpha = -dot(eigs[j], 0, n - 1, curr_vector); 00104 scadd(curr_vector, 0, n - 1, alpha, eigs[j]); 00105 } 00106 len = norm(curr_vector, 0, n - 1); 00107 vecscale(curr_vector, 0, n - 1, 1.0 / len, curr_vector); 00108 evals[i] = 0; 00109 00110 } 00111 00112 00113 /* sort vectors by their evals, for overcoming possible mis-convergence: */ 00114 for (i = 0; i < neigs - 1; i++) { 00115 largest_index = i; 00116 largest_eval = evals[largest_index]; 00117 for (j = i + 1; j < neigs; j++) { 00118 if (largest_eval < evals[j]) { 00119 largest_index = j; 00120 largest_eval = evals[largest_index]; 00121 } 00122 } 00123 if (largest_index != i) { /* exchange eigenvectors: */ 00124 cpvec(tmp_vec, 0, n - 1, eigs[i]); 00125 cpvec(eigs[i], 0, n - 1, eigs[largest_index]); 00126 cpvec(eigs[largest_index], 0, n - 1, tmp_vec); 00127 00128 evals[largest_index] = evals[i]; 00129 evals[i] = largest_eval; 00130 } 00131 } 00132 00133 free(tmp_vec); 00134 free(last_vec); 00135 00136 return (iteration <= Max_iterations); 00137 } 00138 00139 00140 00141 void 00142 mult_dense_mat(double **A, float **B, int dim1, int dim2, int dim3, 00143 float ***CC) 00144 { 00145 /* 00146 A is dim1 x dim2, B is dim2 x dim3, C = A x B 00147 */ 00148 00149 double sum; 00150 int i, j, k; 00151 float *storage; 00152 float **C = *CC; 00153 if (C != NULL) { 00154 storage = (float *) realloc(C[0], dim1 * dim3 * sizeof(A[0])); 00155 *CC = C = (float **) realloc(C, dim1 * sizeof(A)); 00156 } else { 00157 storage = (float *) malloc(dim1 * dim3 * sizeof(A[0])); 00158 *CC = C = (float **) malloc(dim1 * sizeof(A)); 00159 } 00160 00161 for (i = 0; i < dim1; i++) { 00162 C[i] = storage; 00163 storage += dim3; 00164 } 00165 00166 for (i = 0; i < dim1; i++) { 00167 for (j = 0; j < dim3; j++) { 00168 sum = 0; 00169 for (k = 0; k < dim2; k++) { 00170 sum += A[i][k] * B[k][j]; 00171 } 00172 C[i][j] = (float) (sum); 00173 } 00174 } 00175 } 00176 00177 void 00178 mult_dense_mat_d(double **A, float **B, int dim1, int dim2, int dim3, 00179 double ***CC) 00180 { 00181 /* 00182 A is dim1 x dim2, B is dim2 x dim3, C = A x B 00183 */ 00184 double **C = *CC; 00185 double *storage; 00186 int i, j, k; 00187 double sum; 00188 00189 if (C != NULL) { 00190 storage = (double *) realloc(C[0], dim1 * dim3 * sizeof(double)); 00191 *CC = C = (double **) realloc(C, dim1 * sizeof(double *)); 00192 } else { 00193 storage = (double *) malloc(dim1 * dim3 * sizeof(double)); 00194 *CC = C = (double **) malloc(dim1 * sizeof(double *)); 00195 } 00196 00197 for (i = 0; i < dim1; i++) { 00198 C[i] = storage; 00199 storage += dim3; 00200 } 00201 00202 for (i = 0; i < dim1; i++) { 00203 for (j = 0; j < dim3; j++) { 00204 sum = 0; 00205 for (k = 0; k < dim2; k++) { 00206 sum += A[i][k] * B[k][j]; 00207 } 00208 C[i][j] = sum; 00209 } 00210 } 00211 } 00212 00213 void 00214 mult_sparse_dense_mat_transpose(vtx_data * A, double **B, int dim1, 00215 int dim2, float ***CC) 00216 { 00217 /* 00218 A is dim1 x dim1 and sparse, B is dim2 x dim1, C = A x B 00219 */ 00220 00221 float *storage; 00222 int i, j, k; 00223 double sum; 00224 float *ewgts; 00225 int *edges; 00226 int nedges; 00227 float **C = *CC; 00228 if (C != NULL) { 00229 storage = (float *) realloc(C[0], dim1 * dim2 * sizeof(A[0])); 00230 *CC = C = (float **) realloc(C, dim1 * sizeof(A)); 00231 } else { 00232 storage = (float *) malloc(dim1 * dim2 * sizeof(A[0])); 00233 *CC = C = (float **) malloc(dim1 * sizeof(A)); 00234 } 00235 00236 for (i = 0; i < dim1; i++) { 00237 C[i] = storage; 00238 storage += dim2; 00239 } 00240 00241 for (i = 0; i < dim1; i++) { 00242 edges = A[i].edges; 00243 ewgts = A[i].ewgts; 00244 nedges = A[i].nedges; 00245 for (j = 0; j < dim2; j++) { 00246 sum = 0; 00247 for (k = 0; k < nedges; k++) { 00248 sum += ewgts[k] * B[j][edges[k]]; 00249 } 00250 C[i][j] = (float) (sum); 00251 } 00252 } 00253 } 00254 00255 00256 00257 /* Copy a range of a double vector to a double vector */ 00258 void cpvec(double *copy, int beg, int end, double *vec) 00259 { 00260 int i; 00261 00262 copy = copy + beg; 00263 vec = vec + beg; 00264 for (i = end - beg + 1; i; i--) { 00265 *copy++ = *vec++; 00266 } 00267 } 00268 00269 /* Returns scalar product of two double n-vectors. */ 00270 double dot(double *vec1, int beg, int end, double *vec2) 00271 { 00272 int i; 00273 double sum; 00274 00275 sum = 0.0; 00276 vec1 = vec1 + beg; 00277 vec2 = vec2 + beg; 00278 for (i = end - beg + 1; i; i--) { 00279 sum += (*vec1++) * (*vec2++); 00280 } 00281 return (sum); 00282 } 00283 00284 00285 /* Scaled add - fills double vec1 with vec1 + alpha*vec2 over range*/ 00286 void scadd(double *vec1, int beg, int end, double fac, double *vec2) 00287 { 00288 int i; 00289 00290 vec1 = vec1 + beg; 00291 vec2 = vec2 + beg; 00292 for (i = end - beg + 1; i; i--) { 00293 (*vec1++) += fac * (*vec2++); 00294 } 00295 } 00296 00297 /* Scale - fills vec1 with alpha*vec2 over range, double version */ 00298 void vecscale(double *vec1, int beg, int end, double alpha, double *vec2) 00299 { 00300 int i; 00301 00302 vec1 += beg; 00303 vec2 += beg; 00304 for (i = end - beg + 1; i; i--) { 00305 (*vec1++) = alpha * (*vec2++); 00306 } 00307 } 00308 00309 /* Returns 2-norm of a double n-vector over range. */ 00310 double norm(double *vec, int beg, int end) 00311 { 00312 return (sqrt(dot(vec, beg, end, vec))); 00313 } 00314 00315 00316 #ifndef __cplusplus 00317 00318 /* inline */ 00319 void orthog1(int n, double *vec /* vector to be orthogonalized against 1 */ 00320 ) 00321 { 00322 int i; 00323 double *pntr; 00324 double sum; 00325 00326 sum = 0.0; 00327 pntr = vec; 00328 for (i = n; i; i--) { 00329 sum += *pntr++; 00330 } 00331 sum /= n; 00332 pntr = vec; 00333 for (i = n; i; i--) { 00334 *pntr++ -= sum; 00335 } 00336 } 00337 00338 #define RANGE 500 00339 00340 /* inline */ 00341 void init_vec_orth1(int n, double *vec) 00342 { 00343 /* randomly generate a vector orthogonal to 1 (i.e., with mean 0) */ 00344 int i; 00345 00346 for (i = 0; i < n; i++) 00347 vec[i] = rand() % RANGE; 00348 00349 orthog1(n, vec); 00350 } 00351 00352 /* inline */ 00353 void 00354 right_mult_with_vector(vtx_data * matrix, int n, double *vector, 00355 double *result) 00356 { 00357 int i, j; 00358 00359 double res; 00360 for (i = 0; i < n; i++) { 00361 res = 0; 00362 for (j = 0; j < matrix[i].nedges; j++) 00363 res += matrix[i].ewgts[j] * vector[matrix[i].edges[j]]; 00364 result[i] = res; 00365 } 00366 /* orthog1(n,vector); */ 00367 } 00368 00369 /* inline */ 00370 void 00371 right_mult_with_vector_f(float **matrix, int n, double *vector, 00372 double *result) 00373 { 00374 int i, j; 00375 00376 double res; 00377 for (i = 0; i < n; i++) { 00378 res = 0; 00379 for (j = 0; j < n; j++) 00380 res += matrix[i][j] * vector[j]; 00381 result[i] = res; 00382 } 00383 /* orthog1(n,vector); */ 00384 } 00385 00386 /* inline */ 00387 void 00388 vectors_subtraction(int n, double *vector1, double *vector2, 00389 double *result) 00390 { 00391 int i; 00392 for (i = 0; i < n; i++) { 00393 result[i] = vector1[i] - vector2[i]; 00394 } 00395 } 00396 00397 /* inline */ 00398 void 00399 vectors_addition(int n, double *vector1, double *vector2, double *result) 00400 { 00401 int i; 00402 for (i = 0; i < n; i++) { 00403 result[i] = vector1[i] + vector2[i]; 00404 } 00405 } 00406 00407 #ifdef UNUSED 00408 /* inline */ 00409 void 00410 vectors_mult_addition(int n, double *vector1, double alpha, 00411 double *vector2) 00412 { 00413 int i; 00414 for (i = 0; i < n; i++) { 00415 vector1[i] = vector1[i] + alpha * vector2[i]; 00416 } 00417 } 00418 #endif 00419 00420 /* inline */ 00421 void 00422 vectors_scalar_mult(int n, double *vector, double alpha, double *result) 00423 { 00424 int i; 00425 for (i = 0; i < n; i++) { 00426 result[i] = vector[i] * alpha; 00427 } 00428 } 00429 00430 /* inline */ 00431 void copy_vector(int n, double *source, double *dest) 00432 { 00433 int i; 00434 for (i = 0; i < n; i++) 00435 dest[i] = source[i]; 00436 } 00437 00438 /* inline */ 00439 double vectors_inner_product(int n, double *vector1, double *vector2) 00440 { 00441 int i; 00442 double result = 0; 00443 for (i = 0; i < n; i++) { 00444 result += vector1[i] * vector2[i]; 00445 } 00446 00447 return result; 00448 } 00449 00450 /* inline */ 00451 double max_abs(int n, double *vector) 00452 { 00453 double max_val = -1e50; 00454 int i; 00455 for (i = 0; i < n; i++) 00456 if (fabs(vector[i]) > max_val) 00457 max_val = fabs(vector[i]); 00458 00459 return max_val; 00460 } 00461 00462 #ifdef UNUSED 00463 /* inline */ 00464 void orthogvec(int n, double *vec1, /* vector to be orthogonalized */ 00465 double *vec2 /* normalized vector to be orthogonalized against */ 00466 ) 00467 { 00468 double alpha; 00469 if (vec2 == NULL) { 00470 return; 00471 } 00472 00473 alpha = -vectors_inner_product(n, vec1, vec2); 00474 00475 vectors_mult_addition(n, vec1, alpha, vec2); 00476 } 00477 00478 /* sparse matrix extensions: */ 00479 00480 /* inline */ 00481 void mat_mult_vec(vtx_data * L, int n, double *vec, double *result) 00482 { 00483 /* compute result= -L*vec */ 00484 int i, j; 00485 double sum; 00486 int *edges; 00487 float *ewgts; 00488 00489 for (i = 0; i < n; i++) { 00490 sum = 0; 00491 edges = L[i].edges; 00492 ewgts = L[i].ewgts; 00493 for (j = 0; j < L[i].nedges; j++) { 00494 sum -= ewgts[j] * vec[edges[j]]; 00495 } 00496 result[i] = sum; 00497 } 00498 } 00499 #endif 00500 00501 /* inline */ 00502 void 00503 right_mult_with_vector_transpose(double **matrix, 00504 int dim1, int dim2, 00505 double *vector, double *result) 00506 { 00507 /* matrix is dim2 x dim1, vector has dim2 components, result=matrix^T x vector */ 00508 int i, j; 00509 00510 double res; 00511 for (i = 0; i < dim1; i++) { 00512 res = 0; 00513 for (j = 0; j < dim2; j++) 00514 res += matrix[j][i] * vector[j]; 00515 result[i] = res; 00516 } 00517 } 00518 00519 /* inline */ 00520 void 00521 right_mult_with_vector_d(double **matrix, 00522 int dim1, int dim2, 00523 double *vector, double *result) 00524 { 00525 /* matrix is dim1 x dim2, vector has dim2 components, result=matrix x vector */ 00526 int i, j; 00527 00528 double res; 00529 for (i = 0; i < dim1; i++) { 00530 res = 0; 00531 for (j = 0; j < dim2; j++) 00532 res += matrix[i][j] * vector[j]; 00533 result[i] = res; 00534 } 00535 } 00536 00537 00538 /***************************** 00539 ** Single precision (float) ** 00540 ** version ** 00541 *****************************/ 00542 00543 /* inline */ 00544 void orthog1f(int n, float *vec) 00545 { 00546 int i; 00547 float *pntr; 00548 float sum; 00549 00550 sum = 0.0; 00551 pntr = vec; 00552 for (i = n; i; i--) { 00553 sum += *pntr++; 00554 } 00555 sum /= n; 00556 pntr = vec; 00557 for (i = n; i; i--) { 00558 *pntr++ -= sum; 00559 } 00560 } 00561 00562 #ifdef UNUSED 00563 /* inline */ 00564 void right_mult_with_vectorf 00565 (vtx_data * matrix, int n, float *vector, float *result) { 00566 int i, j; 00567 00568 float res; 00569 for (i = 0; i < n; i++) { 00570 res = 0; 00571 for (j = 0; j < matrix[i].nedges; j++) 00572 res += matrix[i].ewgts[j] * vector[matrix[i].edges[j]]; 00573 result[i] = res; 00574 } 00575 } 00576 00577 /* inline */ 00578 void right_mult_with_vector_fd 00579 (float **matrix, int n, float *vector, double *result) { 00580 int i, j; 00581 00582 float res; 00583 for (i = 0; i < n; i++) { 00584 res = 0; 00585 for (j = 0; j < n; j++) 00586 res += matrix[i][j] * vector[j]; 00587 result[i] = res; 00588 } 00589 } 00590 #endif 00591 00592 /* inline */ 00593 void right_mult_with_vector_ff 00594 (float *packed_matrix, int n, float *vector, float *result) { 00595 /* packed matrix is the upper-triangular part of a symmetric matrix arranged in a vector row-wise */ 00596 int i, j, index; 00597 float vector_i; 00598 00599 float res; 00600 for (i = 0; i < n; i++) { 00601 result[i] = 0; 00602 } 00603 for (index = 0, i = 0; i < n; i++) { 00604 res = 0; 00605 vector_i = vector[i]; 00606 /* deal with main diag */ 00607 res += packed_matrix[index++] * vector_i; 00608 /* deal with off diag */ 00609 for (j = i + 1; j < n; j++, index++) { 00610 res += packed_matrix[index] * vector[j]; 00611 result[j] += packed_matrix[index] * vector_i; 00612 } 00613 result[i] += res; 00614 } 00615 } 00616 00617 /* inline */ 00618 void 00619 vectors_substractionf(int n, float *vector1, float *vector2, float *result) 00620 { 00621 int i; 00622 for (i = 0; i < n; i++) { 00623 result[i] = vector1[i] - vector2[i]; 00624 } 00625 } 00626 00627 /* inline */ 00628 void 00629 vectors_additionf(int n, float *vector1, float *vector2, float *result) 00630 { 00631 int i; 00632 for (i = 0; i < n; i++) { 00633 result[i] = vector1[i] + vector2[i]; 00634 } 00635 } 00636 00637 /* inline */ 00638 void 00639 vectors_mult_additionf(int n, float *vector1, float alpha, float *vector2) 00640 { 00641 int i; 00642 for (i = 0; i < n; i++) { 00643 vector1[i] = vector1[i] + alpha * vector2[i]; 00644 } 00645 } 00646 00647 /* inline */ 00648 void vectors_scalar_multf(int n, float *vector, float alpha, float *result) 00649 { 00650 int i; 00651 for (i = 0; i < n; i++) { 00652 result[i] = (float) vector[i] * alpha; 00653 } 00654 } 00655 00656 /* inline */ 00657 void copy_vectorf(int n, float *source, float *dest) 00658 { 00659 int i; 00660 for (i = 0; i < n; i++) 00661 dest[i] = source[i]; 00662 } 00663 00664 /* inline */ 00665 double vectors_inner_productf(int n, float *vector1, float *vector2) 00666 { 00667 int i; 00668 double result = 0; 00669 for (i = 0; i < n; i++) { 00670 result += vector1[i] * vector2[i]; 00671 } 00672 00673 return result; 00674 } 00675 00676 /* inline */ 00677 void set_vector_val(int n, double val, double *result) 00678 { 00679 int i; 00680 for (i = 0; i < n; i++) 00681 result[i] = val; 00682 } 00683 00684 /* inline */ 00685 void set_vector_valf(int n, float val, float* result) 00686 { 00687 int i; 00688 for (i = 0; i < n; i++) 00689 result[i] = val; 00690 } 00691 00692 /* inline */ 00693 double max_absf(int n, float *vector) 00694 { 00695 int i; 00696 float max_val = -1e30f; 00697 for (i = 0; i < n; i++) 00698 if (fabs(vector[i]) > max_val) 00699 max_val = (float) (fabs(vector[i])); 00700 00701 return max_val; 00702 } 00703 00704 /* inline */ 00705 void square_vec(int n, float *vec) 00706 { 00707 int i; 00708 for (i = 0; i < n; i++) { 00709 vec[i] *= vec[i]; 00710 } 00711 } 00712 00713 /* inline */ 00714 void invert_vec(int n, float *vec) 00715 { 00716 int i; 00717 float v; 00718 for (i = 0; i < n; i++) { 00719 if ((v = vec[i]) != 0.0) 00720 vec[i] = 1.0f / v; 00721 } 00722 } 00723 00724 /* inline */ 00725 void sqrt_vec(int n, float *vec) 00726 { 00727 int i; 00728 double d; 00729 for (i = 0; i < n; i++) { 00730 /* do this in two steps to avoid a bug in gcc-4.00 on AIX */ 00731 d = sqrt(vec[i]); 00732 vec[i] = (float) d; 00733 } 00734 } 00735 00736 /* inline */ 00737 void sqrt_vecf(int n, float *source, float *target) 00738 { 00739 int i; 00740 double d; 00741 float v; 00742 for (i = 0; i < n; i++) { 00743 if ((v = source[i]) >= 0.0) { 00744 /* do this in two steps to avoid a bug in gcc-4.00 on AIX */ 00745 d = sqrt(v); 00746 target[i] = (float) d; 00747 } 00748 } 00749 } 00750 00751 /* inline */ 00752 void invert_sqrt_vec(int n, float *vec) 00753 { 00754 int i; 00755 double d; 00756 float v; 00757 for (i = 0; i < n; i++) { 00758 if ((v = vec[i]) > 0.0) { 00759 /* do this in two steps to avoid a bug in gcc-4.00 on AIX */ 00760 d = 1. / sqrt(v); 00761 vec[i] = (float) d; 00762 } 00763 } 00764 } 00765 00766 #ifdef UNUSED 00767 /* inline */ 00768 void init_vec_orth1f(int n, float *vec) 00769 { 00770 /* randomly generate a vector orthogonal to 1 (i.e., with mean 0) */ 00771 int i; 00772 00773 for (i = 0; i < n; i++) 00774 vec[i] = (float) (rand() % RANGE); 00775 00776 orthog1f(n, vec); 00777 } 00778 00779 00780 /* sparse matrix extensions: */ 00781 00782 /* inline */ 00783 void mat_mult_vecf(vtx_data * L, int n, float *vec, float *result) 00784 { 00785 /* compute result= -L*vec */ 00786 int i, j; 00787 float sum; 00788 int *edges; 00789 float *ewgts; 00790 00791 for (i = 0; i < n; i++) { 00792 sum = 0; 00793 edges = L[i].edges; 00794 ewgts = L[i].ewgts; 00795 for (j = 0; j < L[i].nedges; j++) { 00796 sum -= ewgts[j] * vec[edges[j]]; 00797 } 00798 result[i] = sum; 00799 } 00800 } 00801 #endif 00802 00803 #endif
1.7.5