00001
00006 #ifdef HAVE_CONFIG_H
00007 #include <tumble-conf.h>
00008 #endif
00009
00010 #include <iostream>
00011 #include <cmath>
00012 #include <cassert>
00013 #include "util.h"
00014 #include "predicates.h"
00015
00016 using namespace std;
00017
00018 const double ALPHA[7]={0.33333333333333,
00019 0.05971587178978,
00020 0.47014206410511,
00021 0.47014206410511,
00022 0.79742698535310,
00023 0.10128650732345,
00024 0.10128650732345};
00025
00026 const double BETA[7]={ 0.33333333333333,
00027 0.47014206410511,
00028 0.05971587178978,
00029 0.47014206410511,
00030 0.10128650732345,
00031 0.79742698535310,
00032 0.10128650732345};
00033
00034 const double WEIGHT[7]={0.11250000000000,
00035 0.06619707639425,
00036 0.06619707639425,
00037 0.06619707639425,
00038 0.06296959027241,
00039 0.06296959027241,
00040 0.06296959027241};
00041
00042 const double PI = 3.1415926535898;
00043 const double TWOPI = 2.0 * 3.1415926535898;
00044 const double HALFPI = 0.5 * 3.1415926535898;
00045
00046
00047 double Point2D::line_side_test(Point2D b, Point2D c) const
00048 {
00049
00050 Point2D temp(*this);
00051 return orient2d(temp.coords, b.coords, c.coords);
00052 }
00053
00054
00055 bool Point2D::in_circle_test( Point2D a, Point2D b, Point2D c ) const
00056 {
00057
00058 Point2D temp(*this);
00059
00060
00061 if(a.is_left_of(b, c))
00062 return incircle(a.coords, b.coords, c.coords, temp.coords) > 0.0;
00063 else
00064 return incircle(a.coords, c.coords, b.coords, temp.coords) > 0.0;
00065 }
00066
00067
00068
00069 double Point2D::dist_from_line( const Point2D &a, const Point2D &b ) const {
00070 Point2D n( a[ 1 ] - b[ 1 ], b[ 0 ] - a[ 0 ] );
00071 Point2D diff;
00072 double norm = n.mag();
00073 diff = ( *this ) - a;
00074 if ( norm > 0 ) {
00075 n *= 1.0 / norm;
00076 return abs( n.dot( diff ) );
00077 } else {
00078 return diff.mag();
00079 }
00080 }
00081
00082
00083 ostream &operator<<( ostream &stream, const Point2D &p ) {
00084 stream << "<" << p.coords[ 0 ] << "," << p.coords[ 1 ] << ">";
00085 return stream;
00086 }
00087
00088
00089 ostream &operator<<( ostream &stream, const Vec3D &p )
00090 {
00091 stream << "<" << p.coords[ 0 ] << "," << p.coords[ 1 ] << "," << p.coords[2] <<">";
00092 return stream;
00093 }
00094
00095
00096 LinearData::LinearData()
00097 {
00098 length = 0;
00099 fields = NULL;
00100 }
00101
00102 LinearData::LinearData(int l)
00103 {
00104
00105 if(l <=0){
00106 length=0;
00107 fields = NULL;
00108 return;
00109 }
00110
00111 length = l;
00112 if(length > 0){
00113 fields = new double[length];
00114 for(int i=0; i<length; i++) fields[i] = 0.0;
00115 } else {
00116 fields = NULL;
00117 }
00118 }
00119
00120 LinearData::LinearData( double* f, int l )
00121 {
00122
00123
00124 if( l <=0 || f==NULL){
00125 length = 0;
00126 fields = NULL;
00127 }
00128
00129
00130 length = l;
00131 if( length > 0){
00132 fields = new double[ length ];
00133 for ( int i = 0; i < length; i++ ) fields[ i ] = f[ i ];
00134 } else {
00135 fields = NULL;
00136 }
00137 }
00138
00139 LinearData::LinearData( const vector<double> &data)
00140 {
00141 length = data.size();
00142 if( length > 0){
00143 fields = new double[ length ];
00144 for( int i = 0; i < length; i++ ) fields[ i ] = data[ i ];
00145 } else {
00146 fields = NULL;
00147 }
00148 }
00149
00150 LinearData::LinearData( const LinearData &s )
00151 {
00152 assert(this != &s);
00153 if( this != &s ){
00154 length = s.length;
00155 if ( length == 0 ) {
00156 fields = NULL;
00157 } else {
00158 fields = new double[ length ];
00159 for ( int i = 0; i < length; i++ ) fields[ i ] = s.fields[ i ];
00160 }
00161 }
00162 }
00163
00164 LinearData::~LinearData() {
00165 if( fields != NULL && length > 0 ) delete[] fields;
00166 }
00167
00168
00169
00170 LinearData& LinearData::operator=( const LinearData &r )
00171 {
00172 assert(this != &r);
00173 if(this != &r) {
00174 if( fields != NULL && length > 0 ) delete[] fields;
00175 length = r.length;
00176 fields = new double[length];
00177 for ( int i = 0; i < length; i++ ) fields[ i ] = r.fields[ i ];
00178 }
00179 return *this;
00180 }
00181
00182 int LinearData::update( const vector<double> &data )
00183 {
00184 if(data.size() != (unsigned)length){
00185 if ( fields != NULL ) delete[] fields;
00186 length = data.size();
00187 fields = new double[ length ];
00188 }
00189 for ( int i = 0; i < length; i++ ) fields[ i ] = data[ i ];
00190 return length;
00191 }
00192
00193 int LinearData::update( double *f, int l ) {
00194 if(l != length){
00195 if ( fields != NULL ) delete[] fields;
00196 length = l;
00197 fields = new double[ length ];
00198 }
00199 for ( int i = 0; i < length; i++ ) fields[ i ] = f[ i ];
00200 return length;
00201 }
00202
00203 void LinearData::resize( int len)
00204 {
00205 if(len == length ) return;
00206 if( fields != 0 ) delete[] fields;
00207 length = len;
00208 if(length <= 0){
00209 length = 0;
00210 fields = NULL;
00211 } else {
00212 fields = new double[length];
00213 zero();
00214 }
00215 }
00216
00217 void LinearData::zero() {
00218 for(int i=0; i<length; i++) fields[i] = 0.0;
00219 }
00220
00221
00222
00223 LinearData& LinearData::operator+= ( const LinearData &r ) {
00224 assert(r.length == length );
00225 for ( int i = 0; i < length; i++ ) fields[ i ] += r[ i ];
00226 return *this;
00227 }
00228
00229 LinearData& LinearData::operator-= ( const LinearData &r ) {
00230 assert(r.length == length );
00231 for ( int i = 0; i < length; i++ ) fields[ i ] -= r[ i ];
00232 return *this;
00233 }
00234
00235 LinearData& LinearData::operator*= ( double s ) {
00236 for ( int i = 0; i < length; i++ ) fields[ i ] *= s;
00237 return *this;
00238 }
00239
00240 LinearData& LinearData::operator/= ( double s ) {
00241 for ( int i = 0; i < length; i++ ) fields[ i ] /= s;
00242 return *this;
00243 }
00244
00245
00246
00247 LinearData LinearData::operator+ ( const LinearData &r ) const {
00248 assert(r.length == length );
00249 LinearData temp(fields,length);
00250 for ( int i = 0; i < temp.length; i++ ) temp.fields[ i ] += r.fields[ i ];
00251 return temp;
00252 }
00253
00254 LinearData LinearData::operator- ( const LinearData &r ) const {
00255 assert(r.length == length );
00256 LinearData temp(fields,length);
00257 for ( int i = 0; i < temp.length; i++ ) temp.fields[ i ] -= r.fields[ i ];
00258 return temp;
00259 }
00260
00261 LinearData LinearData::operator* ( double s ) const {
00262 LinearData temp(fields,length);
00263 for ( int i = 0; i < temp.length; i++ ) temp.fields[ i ] *= s;
00264 return temp;
00265 }
00266
00267 LinearData LinearData::operator/ ( double s ) const {
00268 LinearData temp(fields,length);
00269 for ( int i = 0; i < temp.length; i++ ) temp.fields[ i ] /= s;
00270 return temp;
00271 }
00272
00273
00274 ostream& operator<<( ostream& stream, const LinearData &d ) {
00275 if ( d.length == 0) {
00276 stream << "<nil>";
00277 return stream;
00278 }
00279
00280
00281
00282
00283 stream << "<";
00284 for ( int i = 0;i < d.length - 1;i++ )
00285 {
00286 stream << d[ i ] << ",";
00287 }
00288 stream << d[ d.length - 1 ] << ">";
00289 return stream;
00290 }
00291
00292
00293
00294
00295 Matrix::Matrix( int r, int c ) {
00296 rows = r;
00297 cols = c;
00298 int i, j;
00299 m = new double*[ rows ];
00300 for ( i = 0;i < rows;i++ ) {
00301 m[ i ] = new double[ cols ];
00302 }
00303 for ( i = 0;i < rows;i++ ) {
00304 for ( j = 0;j < cols;j++ ) {
00305 m[ i ][ j ] = 0.0;
00306 }
00307 }
00308 }
00309
00310 Matrix::Matrix( const Matrix& A ) {
00311 rows = A.rows;
00312 cols = A.cols;
00313
00314 int i, j;
00315 m = new double*[ rows ];
00316 for ( i = 0;i < rows;i++ ) {
00317 m[ i ] = new double[ cols ];
00318 }
00319 for ( i = 0;i < rows;i++ ) {
00320 for ( j = 0;j < cols;j++ ) {
00321 m[ i ][ j ] = A.m[ i ][ j ];
00322 }
00323 }
00324 }
00325
00326
00327 Matrix::~Matrix() {
00328 for ( int i = 0;i < rows;i++ ) {
00329 delete[] m[ i ];
00330 }
00331 delete[] m;
00332 }
00333
00334 Matrix* Matrix::transpose_times_self() {
00335
00336 int i, j, k;
00337 double sum;
00338 Matrix *N = new Matrix( cols, cols );
00339
00340
00341
00342
00343 for ( i = 0;i < cols;i++ ) {
00344 for ( j = 0;j < cols;j++ ) {
00345 sum = 0.0;
00346 for ( k = 0;k < rows;k++ ) {
00347 sum += m[ k ][ i ] * m[ k ][ j ];
00348 }
00349 N->m[ i ][ j ] = sum;
00350 }
00351 }
00352 return N;
00353 }
00354
00355
00356
00357
00358 Matrix* Matrix::inverse() {
00359 if ( rows != cols ) {
00360 cout << "error cannot take inverse of non square matrix!" << endl;
00361 return this;
00362 }
00363 Matrix *N = new Matrix( *this );
00364 int i, j, k;
00365 double q;
00366 double tol = 10e-16;
00367 for ( i = 0;i < cols;i++ ) {
00368 if ( fabs( N->m[ i ][ i ] ) <= tol ) {
00369 return N;
00370 }
00371 q = 1.0 / ( N->m[ i ][ i ] );
00372 N->m[ i ][ i ] = 1.0;
00373 for ( j = 0;j < cols;j++ ) {
00374 N->m[ i ][ j ] *= q;
00375 }
00376 for ( j = 0;j < cols;j++ ) {
00377 if ( i != j ) {
00378 q = N->m[ j ][ i ];
00379 N->m[ j ][ i ] = 0.0;
00380 for ( k = 0;k < cols;k++ ) {
00381 N->m[ j ][ k ] -= q * ( N->m[ i ][ k ] );
00382 }
00383 }
00384 }
00385 }
00386 return N;
00387 }
00388
00389 static double signa[2] = {1.0, -1.0};
00390
00391 Matrix* Matrix::submat( int i, int j ) {
00392 int n, n1, p, p1;
00393 Matrix *S = new Matrix( rows - 1, cols - 1 );
00394
00395 for ( n = n1 = 0; n < rows; n++ ) {
00396 if ( n == i ) continue;
00397 for ( p = p1 = 0; p < cols; p++ ) {
00398 if ( p == j ) continue;
00399 S->m[ n1 ][ p1 ] = m[ n ][ p ];
00400 p1++;
00401 }
00402 n1++;
00403 }
00404 return S;
00405 }
00406
00407
00408 double Matrix::matminor( int i, int j ) {
00409 Matrix * S;
00410 double result;
00411
00412 S = submat( i, j );
00413 result = S->det();
00414 delete S;
00415 return result;
00416 }
00417
00418 double Matrix::cofactor( int i, int j ) {
00419 double result;
00420 result = signa[ ( i + j ) % 2 ] * m[ i ][ j ] * matminor( i, j );
00421 return result;
00422 }
00423
00424 int Matrix::lu_decompose(int *P )
00425 {
00426 int i, j, k, n;
00427 int maxi, tmp;
00428 double c, c1;
00429 int p;
00430
00431 n = cols;
00432 for ( p = 0, i = 0; i < n; i++ ) P[ i ] = i;
00433
00434 for ( k = 0; k < n; k++ ) {
00435 for ( i = k, maxi = k, c = 0.0; i < n; i++ ) {
00436 c1 = fabs( m[ P[ i ] ][ k ] );
00437 if ( c1 > c ) {
00438 c = c1;
00439 maxi = i;
00440 }
00441 }
00442
00443 if ( k != maxi ) {
00444 p++;
00445 tmp = P[ k ];
00446 P[ k ] = P[ maxi ];
00447 P[ maxi ] = tmp;
00448 }
00449
00450
00451
00452
00453 if ( m[ P[ k ] ][ k ] == 0.0 ) return ( -1 );
00454
00455 for ( i = k + 1; i < n; i++ ) {
00456
00457
00458
00459 m[ P[ i ] ][ k ] = m[ P[ i ] ][ k ] / m[ P[ k ] ][ k ];
00460
00461
00462
00463
00464 for ( j = k + 1; j < n; j++ ) {
00465 m[ P[ i ] ][ j ] -= m[ P[ i ] ][ k ] * m[ P[ k ] ][ j ];
00466 }
00467 }
00468 }
00469 return p;
00470 }
00471
00472 double Matrix::det() {
00473 Matrix A(*this);
00474 int *P=new int[rows];
00475 int i, j;
00476 double result;
00477
00478
00479
00480
00481 i = A.lu_decompose( P );
00482 if( i == -1 ){
00483 result = 0.0;
00484 } else {
00485
00486
00487
00488
00489
00490
00491 result = 1.0;
00492 for ( j = 0; j < rows; j++ ) {
00493 result *= A.m[ P[ j ] ][ j ];
00494 }
00495 result *= signa[ i % 2 ];
00496 }
00497 delete[] P;
00498 return result;
00499 }
00500
00501
00502 double* Matrix::transpose_times_vector( double *v ) {
00503 int i, j;
00504 double *x = new double[ cols ];
00505 double sum;
00506
00507 for ( i = 0;i < cols;i++ ) {
00508 sum = 0.0;
00509 for ( j = 0;j < rows;j++ ) {
00510 sum += m[ j ][ i ] * v[ j ];
00511 }
00512 x[ i ] = sum;
00513 }
00514 return x;
00515 }
00516
00517 double* Matrix::times_vector( double *v ) {
00518 int i, j;
00519 double *x = new double[ rows ];
00520 double sum;
00521 for ( i = 0;i < rows;i++ ) {
00522 sum = 0.0;
00523 for ( j = 0;j < cols;j++ ) {
00524 sum += m[ i ][ j ] * v[ j ];
00525 }
00526 x[ i ] = sum;
00527 }
00528 return x;
00529 }
00530
00531
00532 Matrix5::Matrix5( int n ) {
00533 size = n;
00534 d1 = new double[ n - 2 ];
00535 d2 = new double[ n - 1 ];
00536 d3 = new double[ n ];
00537 d4 = new double[ n - 1 ];
00538 d5 = new double[ n - 2 ];
00539 factored = false;
00540 }
00541
00542 Matrix5::Matrix5( const Matrix5& A ) {
00543 size = A.size;
00544 d1 = new double[ size - 2 ];
00545 d2 = new double[ size - 1 ];
00546 d3 = new double[ size ];
00547 d4 = new double[ size - 1 ];
00548 d5 = new double[ size - 2 ];
00549 factored = A.factored;
00550 for ( int i = 0;i < size - 2;i++ ) {
00551 d1[ i ] = A.d1[ i ];
00552 d2[ i ] = A.d2[ i ];
00553 d3[ i ] = A.d3[ i ];
00554 d4[ i ] = A.d4[ i ];
00555 d5[ i ] = A.d5[ i ];
00556 }
00557 d2[ size - 2 ] = A.d2[ size - 2 ];
00558 d3[ size - 2 ] = A.d3[ size - 2 ];
00559 d4[ size - 2 ] = A.d4[ size - 2 ];
00560 d3[ size - 1 ] = A.d3[ size - 1 ];
00561 cout << "Matrix5: copy constructor called!" << endl;
00562 }
00563
00564 Matrix5::~Matrix5() {
00565 delete[] d1;
00566 delete[] d2;
00567 delete[] d3;
00568 delete[] d4;
00569 delete[] d5;
00570 }
00571
00572 Matrix5::Matrix5( const Matrix& M ) {
00573 if ( M.rows != M.cols ) {
00574 cout << "Matrix5::Matrix5(Matrix): Error recieved non square matrix, exiting" << endl;
00575 exit( 1 );
00576 }
00577 int i;
00578 size = M.rows;
00579 factored = false;
00580 d1 = new double[ size - 2 ];
00581 d2 = new double[ size - 1 ];
00582 d3 = new double[ size ];
00583 d4 = new double[ size - 1 ];
00584 d5 = new double[ size - 2 ];
00585
00586
00587
00588 d3[ 0 ] = M.m[ 0 ][ 0 ];
00589 d2[ 0 ] = M.m[ 0 ][ 1 ];
00590 d1[ 0 ] = M.m[ 0 ][ 2 ];
00591
00592
00593 d4[ 0 ] = M.m[ 1 ][ 0 ];
00594 d3[ 1 ] = M.m[ 1 ][ 1 ];
00595 d2[ 1 ] = M.m[ 1 ][ 2 ];
00596 d1[ 1 ] = M.m[ 1 ][ 3 ];
00597
00598
00599 for ( i = 2;i < size - 2;i++ ) {
00600 d5[ i - 2 ] = M.m[ i ][ i - 2 ];
00601 d4[ i - 1 ] = M.m[ i ][ i - 1 ];
00602 d3[ i ] = M.m[ i ][ i ];
00603 d2[ i ] = M.m[ i ][ i + 1 ];
00604 d1[ i ] = M.m[ i ][ i + 2 ];
00605 }
00606
00607
00608 d5[ size - 4 ] = M.m[ size - 2 ][ size - 4 ];
00609 d4[ size - 3 ] = M.m[ size - 2 ][ size - 3 ];
00610 d3[ size - 2 ] = M.m[ size - 2 ][ size - 2 ];
00611 d2[ size - 2 ] = M.m[ size - 2 ][ size - 1 ];
00612
00613
00614 d5[ size - 3 ] = M.m[ size - 1 ][ size - 3 ];
00615 d4[ size - 2 ] = M.m[ size - 1 ][ size - 2 ];
00616 d3[ size - 1 ] = M.m[ size - 1 ][ size - 1 ];
00617 }
00618
00619 void Matrix5::factor() {
00620 int i;
00621 factored = true;
00622
00623 d4[ 0 ] = d4[ 0 ] / d3[ 0 ];
00624 d5[ 0 ] = d5[ 0 ] / d3[ 0 ];
00625
00626
00627 d3[ 1 ] = d3[ 1 ] - d4[ 0 ] * d2[ 0 ];
00628 d4[ 1 ] = ( d4[ 1 ] - d5[ 0 ] * d2[ 0 ] ) / d3[ 1 ];
00629 d5[ 1 ] = d5[ 1 ] / d3[ 1 ];
00630
00631
00632 for ( i = 2;i < ( size - 2 );i++ ) {
00633 d2[ i - 1 ] = d2[ i - 1 ] - d4[ i - 2 ] * d1[ i - 2 ];
00634 d3[ i ] = d3[ i ] - d5[ i - 2 ] * d1[ i - 2 ] - d4[ i - 1 ] * d2[ i - 1 ];
00635 d4[ i ] = ( d4[ i ] - d5[ i - 1 ] * d2[ i - 1 ] ) / d3[ i ];
00636 d5[ i ] = d5[ i ] / d3[ i ];
00637 }
00638
00639
00640 d2[ size - 3 ] = d2[ size - 3 ] - d4[ size - 4 ] * d1[ size - 4 ];
00641 d3[ size - 2 ] = d3[ size - 2 ] - d5[ size - 4 ] * d1[ size - 4 ] - d4[ size - 3 ] * d2[ size - 3 ];
00642 d4[ size - 2 ] = ( d4[ size - 2 ] - d5[ size - 3 ] * d2[ size - 3 ] ) / d3[ size - 2 ];
00643
00644
00645 d2[ size - 2 ] = d2[ size - 2 ] - d4[ size - 3 ] * d1[ size - 3 ];
00646 d3[ size - 1 ] = d3[ size - 1 ] - d5[ size - 3 ] * d1[ size - 3 ] - d4[ size - 2 ] * d2[ size - 2 ];
00647 }
00648
00649
00650
00651
00652 double* Matrix5::LUsolve( double *b ) {
00653 double * y = new double[ size ];
00654 int i;
00655
00656 if ( !factored ) {
00657 factor();
00658 }
00659
00660 y[ 0 ] = b[ 0 ];
00661 y[ 1 ] = b[ 1 ] - y[ 0 ] * d4[ 0 ];
00662 for ( i = 2;i < size;i++ ) {
00663 y[ i ] = b[ i ] - y[ i - 2 ] * d5[ i - 2 ] - y[ i - 1 ] * d4[ i - 1 ];
00664 }
00665
00666
00667 b[ size - 1 ] = y[ size - 1 ] / d3[ size - 1 ];
00668 b[ size - 2 ] = ( y[ size - 2 ] - d2[ size - 2 ] * b[ size - 1 ] ) / d3[ size - 2 ];
00669 for ( i = size - 3;i >= 0;i-- ) {
00670 b[ i ] = ( y[ i ] - d2[ i ] * b[ i + 1 ] - d1[ i ] * b[ i + 2 ] ) / d3[ i ];
00671 }
00672
00673 delete[] y;
00674 return b;
00675 }
00676
00677
00678
00679
00680
00681
00682
00683
00684 ostream& operator<<( ostream& stream, Matrix M ) {
00685 int i, j;
00686 stream << "Matrix:\n";
00687 for ( i = 0;i < M.rows;i++ ) {
00688 stream << "ROW" << i << ": [[";
00689 for ( j = 0;j < M.cols;j++ ) {
00690 stream << M.m[ i ][ j ] << ", ";
00691 }
00692 stream << "]]\n";
00693 }
00694 return stream;
00695 }
00696
00697
00698 int add_filename_extension(char *filename, const char *ext, char *buf, int buf_len)
00699 {
00700 if(!filename || !ext || !buf) return -1;
00701 int ext_len = strlen(ext);
00702 if(buf_len < 2+ext_len) return -1;
00703 if(strlen(filename) + ext_len + 1 > (unsigned) buf_len ) return -1;
00704 bzero(buf, buf_len);
00705
00706 char *e = strrchr(filename,'.');
00707
00708
00709 if(!e){
00710 snprintf(buf,buf_len,"%s.%s",filename, ext);
00711 return 0;
00712 }
00713 e++;
00714
00715
00716 if(strncmp(e,ext,ext_len)){
00717 snprintf(buf,buf_len,"%s.%s",filename, ext);
00718 } else {
00719
00720 strncpy(buf, filename, buf_len);
00721 }
00722 return 0;
00723 }
00724
00725 bool poly_convex(Point2D poly[], int size)
00726 {
00727 for(int i=0; i<size; i++){
00728 if(i < size-2){
00729 if( !poly[i+2].is_left_of(poly[i], poly[i+1]) ) return false;
00730 }else if(i == size-2){
00731 if( !poly[0].is_left_of(poly[i], poly[i+1]) ) return false;
00732 }else{
00733 if( !poly[1].is_left_of(poly[i], poly[0]) ) return false;
00734 }
00735 }
00736 return true;
00737 }
00738
00739 bool point_in_poly_kernel(Point2D poly[], int size, const Point2D &point)
00740 {
00741 for(int i=0; i<size; i++){
00742 if(i < size-1){
00743 if( !point.is_left_of(poly[i], poly[i+1]) ) return false;
00744 }else{
00745 if( !point.is_left_of(poly[i], poly[0]) ) return false;
00746 }
00747 }
00748 return true;
00749 }
00750
00751
00752 ostream& operator << (ostream& os, const ControlPoint& cp)
00753 {
00754 return os << "[CP->" << (void*)(&*cp) << " " << *cp << "]";
00755 }
00756
00757 ostream& operator << (ostream& os, const DataPoint& dp)
00758 {
00759 return os << "[DP->" << (void*)(&*dp) << " " << *dp << "]";
00760 }