nnfw/include/matrices.h
Go to the documentation of this file.
00001 /******************************************************************************** 00002 * Neural Network Framework. * 00003 * Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it> * 00004 * * 00005 * This program is free software; you can redistribute it and/or modify * 00006 * it under the terms of the GNU General Public License as published by * 00007 * the Free Software Foundation; either version 2 of the License, or * 00008 * (at your option) any later version. * 00009 * * 00010 * This program is distributed in the hope that it will be useful, * 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00013 * GNU General Public License for more details. * 00014 * * 00015 * You should have received a copy of the GNU General Public License * 00016 * along with this program; if not, write to the Free Software * 00017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * 00018 ********************************************************************************/ 00019 00020 #ifndef MATRICES_H 00021 #define MATRICES_H 00022 00027 #include "memutils.h" 00028 #include "vectors.h" 00029 #include <exception> 00030 00031 namespace farsa { 00032 00043 class FARSA_NNFW_API DoubleMatrix { 00044 public: 00046 DoubleMatrix( unsigned int rows, unsigned int cols, bool isinternal = false ) { 00047 shData = new sharedData(); 00048 shData->refcounts = 1; 00049 shData->nrows = rows; 00050 shData->ncols = cols; 00051 shData->tsize = rows*cols; 00052 shData->temporary = false; 00053 shData->alldata = new double[ shData->tsize ]; 00054 shData->alldataref = new doubleRef[ shData->tsize ]; 00055 shData->rowdata = new DoubleVector[ rows ]; 00056 shData->coldata = new DoubleVector[ cols ]; 00057 for( unsigned int i=0; i<rows; i++ ) { 00058 // this will also activate isinternal flag on DoubleVectors 00059 shData->rowdata[i].resizeNoData( cols ); 00060 } 00061 for( unsigned int i=0; i<cols; i++ ) { 00062 // this will also activate isinternal flag on DoubleVectors 00063 shData->coldata[i].resizeNoData( rows ); 00064 } 00065 for( unsigned int t=0; t<shData->tsize; t++ ) { 00066 unsigned int r = t/cols; //--- division may be expensive 00067 unsigned int c = t%cols; //--- module may be expensive 00068 shData->rowdata[r].shData->dataref[c].setRef( shData->alldata + t ); 00069 shData->coldata[c].shData->dataref[r].setRef( shData->alldata + t ); 00070 shData->alldataref[t].setRef( shData->alldata + t ); 00071 } 00072 this->isinternal = isinternal; 00073 }; 00075 DoubleMatrix( const DoubleMatrix& src ) { 00076 shData = src.shData; 00077 shData->refcounts++; 00078 //--- is not a temporary anymore ! 00079 shData->temporary = false; 00080 isinternal = false; 00081 }; 00111 DoubleMatrix( unsigned int rows, unsigned int cols, bool temp, unsigned int dummy ) { 00112 (void)dummy; // This stops warnings. Assigning dummy a value would have produce a "parameter ‘dummy’ set but not used" warning 00113 shData = new sharedData(); 00114 shData->refcounts = 1; 00115 shData->nrows = rows; 00116 shData->ncols = cols; 00117 shData->tsize = rows*cols; 00118 shData->temporary = temp; 00119 shData->alldata = new double[ shData->tsize ]; 00120 shData->alldataref = new doubleRef[ shData->tsize ]; 00121 shData->rowdata = new DoubleVector[ rows ]; 00122 shData->coldata = new DoubleVector[ cols ]; 00123 for( unsigned int i=0; i<rows; i++ ) { 00124 // this will also activate isinternal flag on DoubleVectors 00125 shData->rowdata[i].resizeNoData( cols ); 00126 } 00127 for( unsigned int i=0; i<cols; i++ ) { 00128 // this will also activate isinternal flag on DoubleVectors 00129 shData->coldata[i].resizeNoData( rows ); 00130 } 00131 for( unsigned int t=0; t<shData->tsize; t++ ) { 00132 unsigned int r = t/cols; //--- division may be expensive 00133 unsigned int c = t%cols; //--- module may be expensive 00134 shData->rowdata[r].shData->dataref[c].setRef( shData->alldata + t ); 00135 shData->coldata[c].shData->dataref[r].setRef( shData->alldata + t ); 00136 shData->alldataref[t].setRef( shData->alldata + t ); 00137 } 00138 this->isinternal = false; 00139 }; 00141 ~DoubleMatrix() { 00142 shData->refcounts -= 1; 00143 if ( shData->refcounts == 0 ) { 00144 //--- the last destroy the data 00145 if ( shData->tsize > 0 ) { 00146 delete [](shData->rowdata); 00147 delete [](shData->coldata); 00148 delete [](shData->alldataref); 00149 delete [](shData->alldata); 00150 } 00151 delete shData; 00152 } 00153 }; 00155 unsigned int rows() const { 00156 return shData->nrows; 00157 }; 00159 unsigned int cols() const { 00160 return shData->ncols; 00161 }; 00163 unsigned int size() const { 00164 return shData->tsize; 00165 }; 00167 bool operator==( const DoubleMatrix& right ) const { 00168 if ( shData == right.shData ) return true; 00169 if ( shData->tsize != right.shData->tsize ) return false; 00170 if ( shData->nrows != right.shData->nrows ) return false; 00171 if ( shData->ncols != right.shData->ncols ) return false; 00172 for( unsigned int i=0; i<shData->tsize; i++ ) { 00173 if ( shData->alldata[i] != right.shData->alldata[i] ) return false; 00174 } 00175 return true; 00176 }; 00178 bool operator!=( const DoubleMatrix& right ) const { 00179 return !( *this == right ); 00180 }; 00184 DoubleMatrix& resize( unsigned int rows, unsigned int cols ) { 00185 if ( isinternal ) throw MatrixResizeNotAllowed(); 00186 DoubleMatrix newMatrix( rows, cols ); 00187 newMatrix.copyValues( *this ); 00188 //--- the assignment will release the shared data, so there is no need to call detach() 00189 (*this) = newMatrix; 00190 return (*this); 00191 }; 00198 DoubleMatrix& operator=( const DoubleMatrix& src ) { 00199 if ( isinternal ) throw MatrixAssignmentNotAllowed(); 00200 if ( shData == src.shData ) return (*this); 00201 //--- eliminate the previous data 00202 shData->refcounts -= 1; 00203 if ( shData->refcounts == 0 ) { 00204 //--- the last destroy the data 00205 if ( shData->tsize > 0 ) { 00206 delete [](shData->rowdata); 00207 delete [](shData->coldata); 00208 delete [](shData->alldata); 00209 } 00210 delete shData; 00211 } 00212 //--- set the new data taken from src 00213 shData = src.shData; 00214 shData->refcounts++; 00215 //--- is not a temporary anymore ! 00216 shData->temporary = false; 00217 return (*this); 00218 }; 00225 DoubleMatrix& copyValues( const DoubleMatrix& src ) { 00226 // if shared data is the same, they already share the same values 00227 if ( shData == src.shData ) return (*this); 00228 detach(); 00229 unsigned int rowMax = qMin( shData->nrows, src.shData->nrows ); 00230 unsigned int colMax = qMin( shData->ncols, src.shData->ncols ); 00231 for( unsigned int r=0; r<rowMax; r++ ) { 00232 for( unsigned int c=0; c<colMax; c++ ) { 00233 shData->rowdata[r][c] = src.shData->rowdata[r][c]; 00234 } 00235 } 00236 return (*this); 00237 }; 00247 DoubleMatrix& copyValues( const DoubleMatrix& src, unsigned int srcRowOffset, unsigned int srcColOffset, unsigned int thisRowOffset, unsigned int thisColOffset ) { 00248 // if shared data is the same and offsets are zero, then there is nothing to do 00249 if ( shData == src.shData && srcRowOffset == 0 && srcColOffset == 0 && thisRowOffset == 0 && thisColOffset == 0 ) { 00250 return (*this); 00251 } 00252 detach(); 00253 unsigned int rowMax = qMin( shData->nrows-thisRowOffset, src.shData->nrows-srcRowOffset ); 00254 unsigned int colMax = qMin( shData->ncols-thisColOffset, src.shData->ncols-thisColOffset ); 00255 for( unsigned int r=0; r<rowMax; r++ ) { 00256 for( unsigned int c=0; c<colMax; c++ ) { 00257 shData->rowdata[r+thisRowOffset][c+thisColOffset] = src.shData->rowdata[r+srcRowOffset][c+srcColOffset]; 00258 } 00259 } 00260 return (*this); 00261 }; 00275 DoubleMatrix& copyValues( const DoubleMatrix& src, unsigned int srcRowOffset, unsigned int srcColOffset, unsigned int thisRowOffset, unsigned int thisColOffset, unsigned int rowStride, unsigned int colStride ) { 00276 if ( rowStride == 0 || colStride == 0 ) return (*this); 00277 // if shared data is the same and offsets are zero, then there is nothing to do 00278 if ( shData == src.shData && srcRowOffset == 0 && srcColOffset == 0 && thisRowOffset == 0 && thisColOffset == 0 ) { 00279 return (*this); 00280 } 00281 detach(); 00282 unsigned int rowMax = qMin( shData->nrows-thisRowOffset, src.shData->nrows-srcRowOffset ); 00283 unsigned int colMax = qMin( shData->ncols-thisColOffset, src.shData->ncols-thisColOffset ); 00284 for( unsigned int r=0; r<rowMax; r=r+rowStride ) { 00285 for( unsigned int c=0; c<colMax; c=c+colStride ) { 00286 shData->rowdata[r+thisRowOffset][c+thisColOffset] = src.shData->rowdata[r+srcRowOffset][c+srcColOffset]; 00287 } 00288 } 00289 return (*this); 00290 }; 00294 DoubleMatrix& detach() { 00295 if ( shData->refcounts > 1 ) { 00296 sharedData* tmp = new sharedData(); 00297 tmp->refcounts = 1; 00298 tmp->nrows = shData->nrows; 00299 tmp->ncols = shData->ncols; 00300 tmp->tsize = shData->tsize; 00301 tmp->temporary = false; 00302 tmp->alldata = new double[shData->tsize]; 00303 tmp->rowdata = new DoubleVector[ shData->nrows ]; 00304 tmp->coldata = new DoubleVector[ shData->ncols ]; 00305 for( unsigned int i=0; i<shData->nrows; i++ ) { 00306 tmp->rowdata[i].resizeNoData( shData->ncols ); 00307 } 00308 for( unsigned int i=0; i<shData->ncols; i++ ) { 00309 tmp->coldata[i].resizeNoData( shData->nrows ); 00310 } 00311 for( unsigned int t=0; t<tmp->tsize; t++ ) { 00312 unsigned int r = t/( tmp->ncols ); //--- division may be expensive 00313 unsigned int c = t%( tmp->ncols ); //--- module may be expensive 00314 tmp->rowdata[r].shData->dataref[c].setRef( tmp->alldata + t ); 00315 tmp->coldata[c].shData->dataref[r].setRef( tmp->alldata + t ); 00316 } 00317 shData->refcounts--; 00318 shData = tmp; 00319 } 00320 return (*this); 00321 }; 00325 doubleRef& at( unsigned int row, unsigned int col ) { 00326 #ifdef FARSA_DEBUG 00327 if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix"); 00328 if ( col >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix"); 00329 #endif 00330 detach(); 00331 return shData->rowdata[row][col]; 00332 }; 00336 double at( unsigned int row, unsigned int col ) const { 00337 #ifdef FARSA_DEBUG 00338 if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix"); 00339 if ( col >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix"); 00340 #endif 00341 return shData->rowdata[row][col]; 00342 }; 00346 DoubleVector& operator[]( unsigned int row ) { 00347 #ifdef FARSA_DEBUG 00348 if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix"); 00349 #endif 00350 detach(); 00351 return shData->rowdata[row]; 00352 }; 00356 DoubleVector operator[]( unsigned int row ) const { 00357 #ifdef FARSA_DEBUG 00358 if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix"); 00359 #endif 00360 return shData->rowdata[row]; 00361 }; 00363 DoubleVector& row( unsigned int r ) { 00364 #ifdef FARSA_DEBUG 00365 if ( r >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix"); 00366 #endif 00367 detach(); 00368 return shData->rowdata[r]; 00369 }; 00371 DoubleVector row( unsigned int r ) const { 00372 #ifdef FARSA_DEBUG 00373 if ( r >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix"); 00374 #endif 00375 return shData->rowdata[r]; 00376 }; 00378 DoubleVector& column( unsigned int c ) { 00379 #ifdef FARSA_DEBUG 00380 if ( c >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix"); 00381 #endif 00382 detach(); 00383 return shData->coldata[c]; 00384 }; 00386 DoubleVector column( unsigned int c ) const { 00387 #ifdef FARSA_DEBUG 00388 if ( c >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix"); 00389 #endif 00390 return shData->coldata[c]; 00391 }; 00396 DoubleMatrix& steady( unsigned int i, unsigned int j ) { 00397 #ifdef FARSA_DEBUG 00398 if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Fixating elements outside boundary"); 00399 #endif 00400 detach(); 00401 shData->rowdata[i].steady(j); 00402 shData->coldata[j].steady(i); 00403 shData->alldataref[ i*shData->ncols + j ].setSteady(); 00404 return (*this); 00405 }; 00409 DoubleMatrix& unsteady( unsigned int i, unsigned int j ) { 00410 #ifdef FARSA_DEBUG 00411 if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Un-Fixating elements outside boundary"); 00412 #endif 00413 detach(); 00414 shData->rowdata[i].unsteady(j); 00415 shData->coldata[j].unsteady(i); 00416 shData->alldataref[ i*shData->ncols + j ].setNoSteady(); 00417 return (*this); 00418 }; 00420 bool isSteady( unsigned int i, unsigned int j ) const { 00421 #ifdef FARSA_DEBUG 00422 if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing elements outside boundary"); 00423 #endif 00424 return shData->rowdata[i].isSteady(j); 00425 }; 00429 DoubleMatrix& setAll( const double value ) { 00430 detach(); 00431 for( unsigned int i=0; i<shData->tsize; i++ ) { 00432 shData->alldataref[i] = value; 00433 } 00434 return (*this); 00435 }; 00439 DoubleMatrix& setIdentity() { 00440 detach(); 00441 for( unsigned int r=0; r<shData->nrows; r++ ) { 00442 for( unsigned int c=0; c<shData->ncols; c++ ) { 00443 shData->rowdata[r][c] = (r==c); 00444 } 00445 } 00446 return (*this); 00447 }; 00451 DoubleMatrix& zeroing() { 00452 detach(); 00453 for( unsigned int i=0; i<shData->tsize; i++ ) { 00454 shData->alldataref[i] = 0; 00455 } 00456 return (*this); 00457 }; 00461 const DoubleMatrix operator+( const DoubleMatrix& right ) const { 00462 #ifdef FARSA_DEBUG 00463 if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) { 00464 throw IncompatibleMatrices("Incompatibles Matrices in operator+ (dimension must be equals)"); 00465 } 00466 #endif 00467 if ( right.shData->temporary ) { 00468 for( unsigned int i=0; i<shData->tsize; i++ ) { 00469 right.shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i]; 00470 } 00471 return right; 00472 } else if ( shData->temporary ) { 00473 for( unsigned int i=0; i<shData->tsize; i++ ) { 00474 shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i]; 00475 } 00476 return (*this); 00477 } else { 00478 DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 ); 00479 for( unsigned int i=0; i<shData->tsize; i++ ) { 00480 ret.shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i]; 00481 } 00482 return ret; 00483 } 00484 }; 00488 const DoubleMatrix operator-( const DoubleMatrix& right ) const { 00489 #ifdef FARSA_DEBUG 00490 if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) { 00491 throw IncompatibleMatrices("Incompatibles Matrices in operator- (dimension must be equals)"); 00492 } 00493 #endif 00494 if ( right.shData->temporary ) { 00495 for( unsigned int i=0; i<shData->tsize; i++ ) { 00496 right.shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i]; 00497 } 00498 return right; 00499 } else if ( shData->temporary ) { 00500 for( unsigned int i=0; i<shData->tsize; i++ ) { 00501 shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i]; 00502 } 00503 return (*this); 00504 } else { 00505 DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 ); 00506 for( unsigned int i=0; i<shData->tsize; i++ ) { 00507 ret.shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i]; 00508 } 00509 return ret; 00510 } 00511 }; 00516 const DoubleMatrix operator*( const DoubleMatrix& right ) const { 00517 #ifdef FARSA_DEBUG 00518 if ( shData->ncols != right.shData->nrows ) { 00519 throw IncompatibleMatrices("Incompatibles Matrices in operator* (left matrix column dimension must be equal to right matrix row dimension)"); 00520 } 00521 #endif 00522 // FIXME: this is very slow and inefficient way to multiple matrices 00523 DoubleMatrix ret( shData->nrows, right.shData->ncols, true, 0 ); 00524 ret.zeroing(); 00525 for( unsigned int i=0; i<ret.shData->nrows; i++ ) { 00526 for( unsigned int j=0; j<ret.shData->ncols; j++ ) { 00527 for( unsigned int r=0; r<shData->ncols; r++ ) { 00528 ret.shData->rowdata[i][j] += shData->rowdata[i][r] * right.shData->rowdata[r][j]; 00529 } 00530 } 00531 } 00532 return ret; 00533 }; 00537 const DoubleMatrix operator%( const DoubleMatrix& right ) const { 00538 #ifdef FARSA_DEBUG 00539 if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) { 00540 throw IncompatibleMatrices("Incompatibles Matrices in operator% (dimension must be equals)"); 00541 } 00542 #endif 00543 if ( right.shData->temporary ) { 00544 for( unsigned int i=0; i<shData->tsize; i++ ) { 00545 right.shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i]; 00546 } 00547 return right; 00548 } else if ( shData->temporary ) { 00549 for( unsigned int i=0; i<shData->tsize; i++ ) { 00550 shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i]; 00551 } 00552 return (*this); 00553 } else { 00554 DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 ); 00555 for( unsigned int i=0; i<shData->tsize; i++ ) { 00556 ret.shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i]; 00557 } 00558 return ret; 00559 } 00560 }; 00564 const DoubleMatrix operator/( const DoubleMatrix& right ) const { 00565 #ifdef FARSA_DEBUG 00566 if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) { 00567 throw IncompatibleMatrices("Incompatibles Matrices in operator/ (dimension must be equals)"); 00568 } 00569 #endif 00570 if ( right.shData->temporary ) { 00571 for( unsigned int i=0; i<shData->tsize; i++ ) { 00572 right.shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i]; 00573 } 00574 return right; 00575 } else if ( shData->temporary ) { 00576 for( unsigned int i=0; i<shData->tsize; i++ ) { 00577 shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i]; 00578 } 00579 return (*this); 00580 } else { 00581 DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 ); 00582 for( unsigned int i=0; i<shData->tsize; i++ ) { 00583 ret.shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i]; 00584 } 00585 return ret; 00586 } 00587 }; 00591 DoubleMatrix& operator+=( const DoubleMatrix& right ) { 00592 #ifdef FARSA_DEBUG 00593 if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) { 00594 throw IncompatibleMatrices("Incompatibles Matrices in operator += (dimension must be equals)"); 00595 } 00596 #endif 00597 detach(); 00598 for( unsigned int i=0; i<shData->tsize; i++ ) { 00599 shData->alldataref[i] += right.shData->alldata[i]; 00600 } 00601 return (*this); 00602 }; 00606 DoubleMatrix& operator-=( const DoubleMatrix& right ) { 00607 #ifdef FARSA_DEBUG 00608 if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) { 00609 throw IncompatibleMatrices("Incompatibles Matrices in operator -= (dimension must be equals)"); 00610 } 00611 #endif 00612 detach(); 00613 for( unsigned int i=0; i<shData->tsize; i++ ) { 00614 shData->alldataref[i] -= right.shData->alldata[i]; 00615 } 00616 return (*this); 00617 }; 00621 DoubleMatrix& operator%=( const DoubleMatrix& right ) { 00622 #ifdef FARSA_DEBUG 00623 if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) { 00624 throw IncompatibleMatrices("Incompatibles Matrices in operator %= (dimension must be equals)"); 00625 } 00626 #endif 00627 detach(); 00628 for( unsigned int i=0; i<shData->tsize; i++ ) { 00629 shData->alldataref[i] *= right.shData->alldata[i]; 00630 } 00631 return (*this); 00632 }; 00636 DoubleMatrix& operator/=( const DoubleMatrix& right ) { 00637 #ifdef FARSA_DEBUG 00638 if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) { 00639 throw IncompatibleMatrices("Incompatibles Matrices in operator /= (dimension must be equals)"); 00640 } 00641 #endif 00642 detach(); 00643 for( unsigned int i=0; i<shData->tsize; i++ ) { 00644 shData->alldataref[i] /= right.shData->alldata[i]; 00645 } 00646 return (*this); 00647 }; 00648 00649 private: 00650 class sharedData { 00651 public: 00653 unsigned int nrows; 00655 unsigned int ncols; 00657 unsigned int tsize; 00659 double* alldata; 00661 doubleRef* alldataref; 00663 DoubleVector* rowdata; 00665 DoubleVector* coldata; 00667 int refcounts; 00669 bool temporary; 00670 }; 00672 sharedData* shData; 00677 bool isinternal; 00678 }; 00679 00680 } 00681 00682 #endif 00683