1 /********************************************************************** 2 bitvec.cpp - Fast and efficient bitstring class. 3 4 Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc. 5 Some portions Copyright (C) 2001-2006 by Geoffrey R. Hutchison 6 7 This file is part of the Open Babel project. 8 For more information, see <http://openbabel.org/> 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation version 2 of the License. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 ***********************************************************************/ 19 #include <openbabel/babelconfig.h> 20 21 #include <openbabel/bitvec.h> 22 #include <openbabel/oberror.h> 23 #include <cstdlib> 24 25 namespace OpenBabel 26 { 27 28 OBERROR extern OBMessageHandler obErrorLog; 29 30 /*! \class OBBitVec bitvec.h <openbabel/bitvec.h> 31 \brief Fast and efficient bitstring class 32 33 The OBBitVec class is a fast and efficient bitstring class that is 34 handy to use as a truth table. Truth tables are an easy way to store 35 whether a list of items has a particular property. Instances of 36 OBBitVec can be dynamically resized, and have a number of overloaded 37 operators that make code simple and readable. The following examples 38 demonstrate uses of the OBBitVec class: 39 \code 40 OBBitVec bv1,bv2,bv3; 41 bv1.SetBitOn(5); 42 bv2.SetBitOff(200); 43 bv1 |= bv2; 44 bv1 = bv1 & bv2; 45 if (bv1.IsEmpty()) // IsEmpty() returns true if no bits are set on 46 { 47 std::cout << "bv1 = " << bv1 << std::endl; 48 } 49 50 int bit; 51 for (bit = bv1.NextBit(0);bit != bv1.EndBit();bit = bv1.NextBit(bit)) 52 { 53 cout << "the next bit turned on is " << bit << endl; 54 } 55 \endcode 56 */ 57 58 static unsigned int bitsoff[SETWORD] = 59 { 60 0xFFFFFFFF,0xFFFFFFFE,0xFFFFFFFC,0xFFFFFFF8,0xFFFFFFF0,0xFFFFFFE0,0xFFFFFFC0, 61 0xFFFFFF80,0xFFFFFF00,0xFFFFFE00,0xFFFFFC00,0xFFFFF800,0xFFFFF000,0xFFFFE000, 62 0xFFFFC000,0xFFFF8000,0xFFFF0000,0xFFFE0000,0xFFFC0000,0xFFF80000,0xFFF00000, 63 0xFFE00000,0xFFC00000,0xFF800000,0xFF000000,0xFE000000,0xFC000000,0xF8000000, 64 0xF0000000,0xE0000000,0xC0000000,0x80000000 65 }; 66 67 #ifndef LowBit 68 #define LowBit(set, bit) \ 69 {int m; \ 70 if (set != 0) \ 71 { \ 72 bit = 31; \ 73 if (set != 0x80000000) { \ 74 if ((m = (set & 0x0000ffff))!=0) {set = m; bit -= 16;} \ 75 if ((m = (set & 0x00ff00ff))!=0) {set = m; bit -= 8;} \ 76 if ((m = (set & 0x0f0f0f0f))!=0) {set = m; bit -= 4;} \ 77 if ((m = (set & 0x33333333))!=0) {set = m; bit -= 2;} \ 78 if ((m = (set & 0x55555555))!=0) {set = m; bit -= 1;}}} \ 79 else bit = -1;} 80 #endif 81 82 /** Set the \p bit_offset 'th bit to 1 83 Increases the size of this bit vector if necessary 84 \param[in] bit_offset a zero based offset into the bit vector 85 */ SetBitOn(unsigned bit_offset)86 void OBBitVec::SetBitOn(unsigned bit_offset) 87 { 88 unsigned word_offset = bit_offset >> WORDROLL; 89 bit_offset &= WORDMASK; 90 91 if (word_offset >= GetSize()) 92 ResizeWords(word_offset + 1); 93 _set[word_offset] |= (1<<bit_offset); 94 } 95 96 /** Set the \p bit_offset 'th bit to 0 97 \param[in] bit_offset a zero based offset into the bit vector 98 */ SetBitOff(unsigned bit_offset)99 void OBBitVec::SetBitOff(unsigned bit_offset) 100 { 101 unsigned word_offset = bit_offset >> WORDROLL; 102 bit_offset &= WORDMASK; 103 104 if (word_offset < GetSize()) 105 _set[word_offset] &= (~(1 << bit_offset)); 106 } 107 108 /** Set the range of bits from \p lo_bit_offset to \p hi_bit_offset to 1 109 Increases the size of this bit vector if necessary 110 \param[in] lo_bit_offset a zero based offset into the bit vector 111 \param[in] hi_bit_offset a zero based offset into the bit vector 112 */ SetRangeOn(unsigned lo_bit_offset,unsigned hi_bit_offset)113 void OBBitVec::SetRangeOn(unsigned lo_bit_offset, unsigned hi_bit_offset) 114 { 115 if (lo_bit_offset > hi_bit_offset) 116 return; 117 else if (lo_bit_offset == hi_bit_offset) 118 SetBitOn(hi_bit_offset); 119 else 120 { 121 unsigned lo_word_offset = lo_bit_offset >> WORDROLL; 122 unsigned hi_word_offset = hi_bit_offset >> WORDROLL; 123 lo_bit_offset &= WORDMASK; 124 hi_bit_offset &= WORDMASK; 125 126 if (hi_word_offset >= GetSize()) 127 ResizeWords(hi_word_offset + 1); 128 129 if (lo_word_offset == hi_word_offset) 130 { 131 for ( unsigned i = lo_bit_offset ; i <= hi_bit_offset ; i++ ) 132 _set[lo_word_offset] |= (1<<i); 133 } 134 else 135 { 136 for ( unsigned i = lo_bit_offset ; i < SETWORD ; ++ i ) 137 _set[lo_word_offset] |= (1<<i); 138 for ( unsigned i = lo_word_offset + 1 ; i < hi_word_offset ; ++ i ) 139 _set[i] = ~0; 140 for ( unsigned i = 0 ; i <= hi_bit_offset ; ++ i ) 141 _set[hi_word_offset] |= (1<<i); 142 } 143 } 144 } 145 146 /** Set the range of bits from \p lo_bit_offset to \p hi_bit_offset to 0 147 \param[in] lo_bit_offset a zero based offset into the bit vector 148 \param[in] hi_bit_offset a zero based offset into the bit vector 149 */ SetRangeOff(unsigned lo_bit_offset,unsigned hi_bit_offset)150 void OBBitVec::SetRangeOff(unsigned lo_bit_offset, unsigned hi_bit_offset) 151 { 152 if (lo_bit_offset > hi_bit_offset) 153 return; 154 else if (lo_bit_offset == hi_bit_offset) 155 SetBitOff(hi_bit_offset); 156 else 157 { 158 unsigned lo_word_offset = lo_bit_offset >> WORDROLL; 159 unsigned hi_word_offset = hi_bit_offset >> WORDROLL; 160 lo_bit_offset &= WORDMASK; 161 hi_bit_offset &= WORDMASK; 162 163 if (lo_word_offset >= GetSize()) 164 return; 165 if (hi_word_offset >= GetSize()) 166 { 167 hi_word_offset = GetSize() - 1; 168 hi_bit_offset = SETWORD - 1; 169 } 170 171 if (lo_word_offset == hi_word_offset) 172 { 173 for ( unsigned i = lo_bit_offset ; i <= hi_bit_offset ; ++ i ) 174 _set[lo_word_offset] &= (~(1<<i)); 175 } 176 else 177 { 178 for ( unsigned i = lo_bit_offset ; i < SETWORD ; ++ i ) 179 _set[lo_word_offset] &= (~(1<<i)); 180 for ( unsigned i = lo_word_offset + 1 ; i < hi_word_offset ; ++ i ) 181 _set[i] = 0x00000000; 182 for ( unsigned i = 0 ; i <= hi_bit_offset ; ++ i ) 183 _set[hi_word_offset] &= (~(1<<i)); 184 } 185 } 186 } 187 188 /** Reduce the size of the vector to \p new_bit_size 189 by or-ing the excess bits over the start of the vector 190 \param[in] new_bit_size the size of the resultant vector, in bits 191 */ Fold(unsigned new_bit_size)192 void OBBitVec::Fold(unsigned new_bit_size) 193 { 194 unsigned new_word_size = new_bit_size >> WORDROLL; 195 196 if (_size < new_word_size) 197 { 198 ResizeWords(new_word_size); 199 return; 200 } 201 202 for (unsigned i = 0, idx = new_word_size; idx < _size; ++idx ) 203 { 204 _set[i] |= _set[idx]; 205 if (i+1 < new_word_size) 206 ++i; 207 else 208 i = 0; 209 } 210 ResizeWords(new_word_size); 211 } 212 213 /** Searches the vector for the first true value, starting at the \p last_bit_offset 'th bit 214 \param[in] last_bit_offset the bit before the first to consider 215 \return the bit offset of the first true bit after \p last_bit_offset, or -1 if there is none 216 */ NextBit(int last_bit_offset) const217 int OBBitVec::NextBit(int last_bit_offset) const 218 { 219 unsigned s; 220 int bit; 221 unsigned wrdcnt; 222 ++ last_bit_offset; 223 224 wrdcnt = (unsigned)last_bit_offset >> WORDROLL; 225 226 if (wrdcnt >= GetSize()) 227 return(-1); 228 229 if (_set[wrdcnt] != 0) 230 { 231 s = _set[wrdcnt] & bitsoff[last_bit_offset & WORDMASK]; 232 if (s) 233 { 234 LowBit(s,bit); 235 if (bit != -1) 236 return(bit + (wrdcnt << WORDROLL)); 237 } 238 } 239 ++ wrdcnt; 240 241 while(wrdcnt < GetSize()) 242 { 243 if (_set[wrdcnt] != 0) 244 { 245 s = _set[wrdcnt]; 246 LowBit(s, bit); 247 248 if (bit != -1) 249 return(bit + (wrdcnt << WORDROLL)); 250 } 251 ++ wrdcnt; 252 } 253 254 return(-1); 255 } 256 // Used by CountBits 257 const unsigned nibble_bit_count[0x10] = 258 { 259 0, // 0000 260 1, // 0001 261 1, // 0010 262 2, // 0011 263 1, // 0100 264 2, // 0101 265 2, // 0110 266 3, // 0111 267 1, // 1000 268 2, // 1001 269 2, // 1010 270 3, // 1011 271 2, // 1100 272 3, // 1101 273 3, // 1110 274 4 // 1111 275 }; 276 /** Count the number of bits which are set in this vector 277 \return the bit count 278 */ CountBits() const279 unsigned OBBitVec::CountBits() const 280 { 281 unsigned count = 0; 282 for (word_vector::const_iterator sx = _set.begin(), sy = _set.end(); sx != sy; ++ sx) 283 { 284 uint32_t word = *sx; 285 while (word) 286 { 287 count += nibble_bit_count[word & 0xF]; 288 word >>= 4; 289 } 290 } 291 return count; 292 } 293 294 /** Are there no bits set to 1 in this vector? 295 \return true for "is empty", false if not empty 296 */ IsEmpty() const297 bool OBBitVec::IsEmpty() const 298 { 299 for (word_vector::const_iterator sx = _set.begin(), sy = _set.end(); sx != sy; ++ sx) 300 if (* sx) 301 return(false); 302 303 return(true); 304 } 305 306 /** Sets bits on, listed as bit offsets 307 \param[in] bit_offsets A list of bit offsets 308 */ FromVecInt(const std::vector<int> & bit_offsets)309 void OBBitVec::FromVecInt(const std::vector<int> & bit_offsets) 310 { 311 for (std::vector<int>::const_iterator i = bit_offsets.begin(), j = bit_offsets.end(); i != j; ++i) 312 SetBitOn(* i); 313 } 314 /** Sets bits on, listed as a string of character-represented integers 315 This bit vector is first cleared. 316 The format is "[ n0 n1 n2 n3 ... ]". 317 The square brackets are optional. 318 The whitespace can be SPACE, NEWLINE or HTAB 319 For example "[ 1 5 6 9 ]" 320 \param[in] line A string containing positive integers 321 \param[in] new_bit_size The size that the vector should become 322 */ FromString(const std::string & line,int new_bit_size)323 void OBBitVec::FromString(const std::string & line, int new_bit_size) 324 { 325 size_t startpos = 0, endpos = 0; 326 std::vector<std::string> tokens; 327 328 Clear(); 329 Resize(new_bit_size); // new bits are clear 330 331 for (;;) 332 { 333 startpos = line.find_first_not_of(" \t\r\n",startpos); 334 endpos = line.find_first_of(" \t\r\n",startpos); 335 if (endpos == std::string::npos) 336 endpos = line.size(); 337 338 if (startpos <= line.size()) 339 tokens.push_back(line.substr(startpos,endpos-startpos)); 340 else 341 break; 342 343 startpos = endpos + 1; 344 } 345 346 for (unsigned int i = 0 ; i < tokens.size() ; i++ ) 347 { 348 if ( tokens[i] == "[" ) 349 continue; 350 else if ( tokens[i] == "]" ) 351 break; 352 353 int bit = atoi(tokens[i].c_str()); 354 355 if (bit >= 0) 356 SetBitOn(bit); 357 else 358 { 359 std::stringstream errorMsg; 360 errorMsg << "Negative Bit: " << bit << std::endl; 361 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obDebug); 362 } 363 } 364 } 365 366 /** Retrieve a list of bit offsets 367 The \p bit_offsets vector is first cleared. 368 \param[out] bit_offsets A list of bit offsets, in ascending order 369 */ ToVecInt(std::vector<int> & bit_offsets) const370 void OBBitVec::ToVecInt(std::vector<int> & bit_offsets) const 371 { 372 bit_offsets.clear(); 373 bit_offsets.reserve(CountBits()); 374 for (int i = NextBit(-1);i != -1;i = NextBit(i)) 375 bit_offsets.push_back(i); 376 } 377 378 /** Set all the bits in this vector to zero 379 Does not currently change the size of the vector. 380 */ Clear()381 void OBBitVec::Clear() 382 { 383 for (word_vector::iterator wx = _set.begin(), wy = _set.end(); wx != wy; ++wx) 384 * wx = 0; 385 } 386 387 /** Assign this vector to be a copy of \p bv 388 \param[in] bv A bit vector 389 \return A reference to this 390 */ operator =(const OBBitVec & bv)391 OBBitVec & OBBitVec::operator= (const OBBitVec & bv) 392 { 393 _set = bv._set; 394 _size = _set.size(); 395 return(*this); 396 } 397 398 /** Assign this vector to the result of And-ing it with \p bv 399 \param[in] bv A bit vector 400 \return A reference to this 401 */ operator &=(const OBBitVec & bv)402 OBBitVec & OBBitVec::operator&= (const OBBitVec & bv) 403 { 404 unsigned min = (bv.GetSize() < _size) ? bv.GetSize() : _size; 405 unsigned i; 406 407 for (i = 0;i < min;++i) 408 _set[i] &= bv._set[i]; 409 for (;i < _size;++i) 410 _set[i] = 0; 411 412 return(*this); 413 } 414 415 /** Assign this vector to the result of Or-ing it with \p bv 416 \param[in] bv A bit vector 417 \return A reference to this 418 */ operator |=(const OBBitVec & bv)419 OBBitVec & OBBitVec::operator|= (const OBBitVec & bv) 420 { 421 if (_size < bv.GetSize()) 422 ResizeWords(bv.GetSize()); 423 424 for (unsigned i = 0;i < bv.GetSize(); ++i) 425 _set[i] |= bv._set[i]; 426 427 return(*this); 428 } 429 430 /** Assign this vector to the result of Exclusive-or-ing it with \p bv 431 \param[in] bv A bit vector 432 \return A reference to this 433 */ operator ^=(const OBBitVec & bv)434 OBBitVec & OBBitVec::operator^= (const OBBitVec & bv) 435 { 436 if (_size < bv.GetSize()) 437 ResizeWords(bv.GetSize()); 438 439 for (unsigned i = 0;i < bv.GetSize(); ++i) 440 _set[i] ^= bv._set[i]; 441 442 return(*this); 443 } 444 445 /** Unset bits in this vector which are set in \p bv 446 \param[in] bv A bit vector 447 \return A reference to this 448 */ operator -=(const OBBitVec & bv)449 OBBitVec & OBBitVec::operator-= (const OBBitVec & bv) 450 { 451 if (_size < bv.GetSize()) 452 ResizeWords(bv.GetSize()); 453 454 OBBitVec tmp(*this); 455 tmp ^= bv; 456 *this &= tmp; 457 return(*this); 458 } 459 460 /** Append vector \p bv to the end if this vector 461 \param[in] bv A bit vector 462 \return A reference to this 463 */ operator +=(const OBBitVec & bv)464 OBBitVec & OBBitVec::operator+= (const OBBitVec & bv) 465 { 466 _set.insert(_set.end(), bv._set.begin(), bv._set.end()); 467 return(*this); 468 } 469 470 /** Return a bit vector of the results of Or-ing each bit in \p bv1 with the corresponding bit in \p bv2 471 \param[in] bv1 A bit vector 472 \param[in] bv2 Another bit vector 473 \return A bit vector 474 */ operator |(const OBBitVec & bv1,const OBBitVec & bv2)475 OBBitVec operator| (const OBBitVec & bv1, const OBBitVec & bv2) 476 { 477 OBBitVec bv(bv1); 478 bv |= bv2; 479 return(bv); 480 } 481 482 /** Return a bit vector of the results of And-ing each bit in \p bv1 with the corresponding bit in \p bv2 483 \param[in] bv1 A bit vector 484 \param[in] bv2 Another bit vector 485 \return A bit vector 486 */ operator &(const OBBitVec & bv1,const OBBitVec & bv2)487 OBBitVec operator& (const OBBitVec & bv1, const OBBitVec & bv2) 488 { 489 OBBitVec bv(bv1); 490 bv &= bv2; 491 return(bv); 492 } 493 494 /** Return a bit vector of the results of Exclusive-or-ing each bit in \p bv1 with the corresponding bit in \p bv2 495 \param[in] bv1 A bit vector 496 \param[in] bv2 Another bit vector 497 \return A bit vector 498 */ operator ^(const OBBitVec & bv1,const OBBitVec & bv2)499 OBBitVec operator^ (const OBBitVec & bv1, const OBBitVec & bv2) 500 { 501 OBBitVec bv(bv1); 502 bv ^= bv2; 503 return(bv); 504 } 505 506 /** Return a bit vector of the results of clearing each bit in \p bv1 which is set in \p bv2 507 \param[in] bv1 A bit vector 508 \param[in] bv2 Another bit vector 509 \return A bit vector 510 */ operator -(const OBBitVec & bv1,const OBBitVec & bv2)511 OBBitVec operator- (const OBBitVec & bv1, const OBBitVec & bv2) 512 { 513 OBBitVec bv; 514 bv = bv1 ^ bv2; 515 bv &= bv1; 516 return(bv); 517 } 518 519 /** Return true if \p bv1 and \p bv2 are equivalent 520 Not that they may be of different size, and still equivalent provided that the extra bits are all zero. 521 \param[in] bv1 A bit vector 522 \param[in] bv2 Another bit vector 523 \return true if equal, false otherwise 524 */ operator ==(const OBBitVec & bv1,const OBBitVec & bv2)525 bool operator== (const OBBitVec & bv1, const OBBitVec & bv2) 526 { 527 if (bv1.GetSize() < bv2.GetSize()) 528 { // bv1 smaller than bv2 529 unsigned i; 530 for (i = 0; i < bv1.GetSize(); ++ i) 531 if (bv1._set[i] != bv2._set[i]) 532 return false; 533 for (; i < bv2.GetSize(); ++ i) 534 if (bv2._set[i] != 0) 535 return false; 536 } 537 else 538 { // bv2 smaller or equal than bv1 539 unsigned i; 540 for (i = 0; i < bv2.GetSize(); ++ i) 541 if (bv1._set[i] != bv2._set[i]) 542 return false; 543 for (; i < bv1.GetSize(); ++ i) 544 if (bv1._set[i] != 0) 545 return false; 546 } 547 return true; 548 } 549 550 /** Return true if \p bv1 i less than \p bv2 551 Lexicographical order, with bit vectors written LSB first. 552 \param[in] bv1 A bit vector 553 \param[in] bv2 Another bit vector 554 \return true if equal, false otherwise 555 */ operator <(const OBBitVec & bv1,const OBBitVec & bv2)556 bool operator< (const OBBitVec & bv1, const OBBitVec & bv2) 557 { 558 bool rtn = false; 559 int next_bit_1 = bv1.NextBit(-1); 560 int next_bit_2 = bv2.NextBit(-1); 561 bool should_continue = true; 562 while (should_continue) 563 { 564 should_continue = false; 565 if (next_bit_1 == -1) 566 rtn = (next_bit_2 == -1 ? false : true); 567 else if (next_bit_2 == -1) 568 rtn = false; 569 else if (next_bit_2 < next_bit_1) 570 rtn = true; 571 else if (next_bit_1 < next_bit_2) 572 rtn = false; 573 else 574 { 575 next_bit_1 = bv1.NextBit(next_bit_1); 576 next_bit_2 = bv2.NextBit(next_bit_2); 577 should_continue = true; 578 } 579 } 580 return rtn; 581 } 582 583 /** Sets bits on, listed as a string of character-represented integers in a stream 584 Only reads one line of input 585 The format is "[ n0 n1 n2 n3 ... ]". 586 The square brackets are optional. 587 The whitespace can be SPACE or HTAB 588 For example "[ 1 5 6 9 ]" 589 \param[in,out] is The input stream 590 \param[out] bv The bit vector to contain the result 591 */ operator >>(std::istream & is,OBBitVec & bv)592 std::istream & operator>> ( std::istream & is, OBBitVec & bv ) 593 { 594 size_t startpos = 0, endpos = 0; 595 std::vector<std::string> tokens; 596 std::string line; 597 598 getline(is,line); 599 600 for (;;) 601 { 602 startpos = line.find_first_not_of(" \t\r\n",startpos); 603 endpos = line.find_first_of(" \t\r\n",startpos); 604 605 if (endpos < line.size() && startpos <= line.size()) 606 tokens.push_back(line.substr(startpos,endpos-startpos)); 607 else 608 break; 609 610 startpos = endpos + 1; 611 } 612 613 for (unsigned int i = 0 ; i < tokens.size() ; i++ ) 614 { 615 if ( tokens[i] == "[" ) 616 continue; 617 else if ( tokens[i] == "]" ) 618 break; 619 620 int bit = atoi(tokens[i].c_str()); 621 622 if (bit >= 0) 623 bv.SetBitOn(bit); 624 else 625 { 626 std::stringstream errorMsg; 627 errorMsg << "Negative Bit: " << bit << std::endl; 628 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obDebug); 629 } 630 } 631 632 return is; 633 } 634 635 /** Output this bit vector to a stream 636 The format is "[ n0 n1 n2 n3 ... ]". 637 The whitespace is SPACE 638 For example "[ 1 5 6 9 ]" 639 \param[out] os The output stream 640 \param[in] bv The bit vector to be output 641 */ operator <<(std::ostream & os,const OBBitVec & bv)642 std::ostream & operator<< ( std::ostream & os, const OBBitVec & bv) 643 { 644 os << "[ " << std::flush; 645 646 for (unsigned i = 0;i < bv._size;++i) 647 for (unsigned j = 0;j < SETWORD;++j) 648 if (bv._set[i]>>(j%SETWORD)&1) 649 os << (j+(i*SETWORD)) << ' ' << std::flush; 650 651 os << "]" << std::flush; 652 return(os); 653 } 654 655 /** The Tanimoto coefficient may be regarded as the proportion of the "on-bits" which are shared. 656 \param[in] bv1 the first bit vector 657 \param[in] bv2 the second bit vector 658 \return the ratio of shared bits to bits which either vector has set. 659 */ Tanimoto(const OBBitVec & bv1,const OBBitVec & bv2)660 double Tanimoto(const OBBitVec & bv1, const OBBitVec & bv2) 661 { 662 OBBitVec bvtmp; 663 double andbits,orbits; 664 665 bvtmp = bv1 & bv2; 666 andbits = (double)bvtmp.CountBits(); 667 bvtmp = bv1 | bv2; 668 orbits = (double)bvtmp.CountBits(); 669 670 return(andbits/orbits); 671 } 672 673 } // end namespace OpenBabel 674 675 //! \file bitvec.cpp 676 //! \brief Fast and efficient bitstring class 677 678