|
GiNaC
1.6.2
|
00001 00005 /* 00006 * GiNaC Copyright (C) 1999-2011 Johannes Gutenberg University Mainz, Germany 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 #ifndef GINAC_HASH_MAP_H 00024 #define GINAC_HASH_MAP_H 00025 00026 #include <algorithm> 00027 #include <functional> 00028 #include <iterator> 00029 #include <list> 00030 #include <utility> 00031 00032 namespace GiNaC { 00033 00034 /* 00035 * "Hashmap Light" - buckets only contain one value, quadratic probing, 00036 * grows automatically 00037 */ 00038 00039 namespace internal { 00040 00041 // List of prime numbers shamelessly stolen from GCC STL 00042 enum { num_primes = 29 }; 00043 00044 static const unsigned long prime_list[num_primes] = 00045 { 00046 31ul, 53ul, 97ul, 193ul, 389ul, 00047 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 00048 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 00049 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, 00050 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 00051 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul 00052 }; 00053 00054 inline unsigned long next_prime(unsigned long n) 00055 { 00056 const unsigned long *first = prime_list; 00057 const unsigned long *last = prime_list + num_primes; 00058 const unsigned long *pos = std::lower_bound(first, last, n); 00059 return pos == last ? *(last - 1) : *pos; 00060 } 00061 00062 } // namespace internal 00063 00064 00065 // Define default arguments 00066 template <typename T, template <class> class A = std::allocator> 00067 class exhashmap; 00068 00069 00081 template <typename T, template <class> class A> 00082 class exhashmap { 00083 public: 00084 static const unsigned min_num_buckets = 31; // must be prime 00085 00086 // Standard types 00087 typedef ex key_type; 00088 typedef T mapped_type; 00089 typedef std::pair<key_type, T> value_type; 00090 typedef ex_is_less key_compare; 00091 typedef ex_is_equal key_equal; 00092 typedef value_type & reference; 00093 typedef const value_type & const_reference; 00094 typedef value_type * pointer; 00095 typedef const value_type * const_pointer; 00096 00097 protected: 00098 // Private types 00099 enum bucket_state { 00100 EMPTY, 00101 USED, 00102 ERASED 00103 }; 00104 typedef std::pair<bucket_state, value_type> Bucket; 00105 00106 public: 00107 // More standard types 00108 typedef A<Bucket> allocator_type; 00109 00110 protected: 00111 // More private types 00112 typedef std::vector<Bucket, allocator_type> Table; 00113 00114 typedef typename Table::iterator table_iterator; 00115 typedef typename Table::const_iterator table_const_iterator; 00116 00117 public: 00118 // Iterators 00119 template <typename Pointer, typename Reference, class TableIterator> 00120 class exhashmap_iterator : public std::iterator<std::forward_iterator_tag, value_type, typename Table::difference_type, Pointer, Reference> { 00121 protected: 00122 friend class exhashmap; 00123 00124 public: 00125 exhashmap_iterator() {} 00126 exhashmap_iterator(TableIterator t, TableIterator te) 00127 : it(t), table_end(te) {} 00128 00129 // Allow iterator to const_iterator conversion 00130 template <typename P, typename R, class TI> 00131 exhashmap_iterator(const exhashmap_iterator<P, R, TI> &other) 00132 : it(other.get_it_()), table_end(other.get_table_end_()) {} 00133 00134 typename exhashmap_iterator::reference operator*() const 00135 { 00136 return it->second; 00137 } 00138 00139 typename exhashmap_iterator::pointer operator->() const 00140 { 00141 return &(it->second); 00142 } 00143 00144 exhashmap_iterator &operator++() 00145 { 00146 increment(); 00147 return *this; 00148 } 00149 00150 exhashmap_iterator operator++(int) 00151 { 00152 exhashmap_iterator tmp = *this; 00153 increment(); 00154 return tmp; 00155 } 00156 00157 template <typename P, typename R, class TI> 00158 bool operator==(const exhashmap_iterator<P, R, TI> &other) const 00159 { 00160 return it == other.get_it_(); 00161 } 00162 00163 template <typename P, typename R, class TI> 00164 bool operator!=(const exhashmap_iterator<P, R, TI> &other) const 00165 { 00166 return it != other.get_it_(); 00167 } 00168 00169 // Private access function 00170 TableIterator get_it_() const { return it; } 00171 TableIterator get_table_end_() const { return table_end; } 00172 00173 protected: 00174 TableIterator it; 00175 TableIterator table_end; 00176 00177 void increment() 00178 { 00179 if (it != table_end) 00180 ++it; 00181 00182 // Skip empty and erased buckets 00183 while (it != table_end && it->first != USED) 00184 ++it; 00185 } 00186 }; 00187 00188 typedef exhashmap_iterator<value_type*, value_type&, table_iterator> iterator; 00189 typedef exhashmap_iterator<const value_type*, const value_type&, table_const_iterator> const_iterator; 00190 00191 // More standard types 00192 typedef typename Table::size_type size_type; 00193 typedef typename Table::difference_type difference_type; 00194 00195 class value_compare : public std::binary_function<value_type, value_type, bool>, private key_compare { 00196 friend class exhashmap; 00197 public: 00198 bool operator()(const value_type &lhs, const value_type &rhs) const 00199 { 00200 return key_compare::operator()(lhs.first, rhs.first); 00201 } 00202 00203 bool operator()(const key_type &lhs, const value_type &rhs) const 00204 { 00205 return key_compare::operator()(lhs, rhs.first); 00206 } 00207 00208 bool operator()(const value_type &lhs, const key_type &rhs) const 00209 { 00210 return key_compare::operator()(lhs.first, rhs); 00211 } 00212 }; 00213 00214 protected: 00215 // Private data 00216 size_type num_entries; 00217 size_type num_buckets; 00218 Table hashtab; 00219 00221 static size_type hash_index(const key_type &x, size_type nbuckets) 00222 { 00223 return x.gethash() % nbuckets; 00224 } 00225 00226 static table_iterator find_bucket(const key_type &x, table_iterator tab, size_type nbuckets); 00227 static table_const_iterator find_bucket(const key_type &x, table_const_iterator tab, size_type nbuckets); 00228 static table_iterator find_bucket_for_insertion(const key_type &x, table_iterator tab, size_type nbuckets); 00229 00231 table_iterator find_bucket(const key_type &x) 00232 { 00233 return find_bucket(x, hashtab.begin(), num_buckets); 00234 } 00235 00237 table_const_iterator find_bucket(const key_type &x) const 00238 { 00239 return find_bucket(x, hashtab.begin(), num_buckets); 00240 } 00241 00243 table_iterator find_bucket_for_insertion(const key_type &x) 00244 { 00245 return find_bucket_for_insertion(x, hashtab.begin(), num_buckets); 00246 } 00247 00249 size_type hwm() const 00250 { 00251 // Try to keep at least 25% of the buckets free 00252 return num_buckets - (num_buckets >> 2); 00253 } 00254 00255 void grow(); 00256 00257 public: 00258 // 23.3.1.1 Construct/copy/destroy 00259 exhashmap() 00260 : num_entries(0), num_buckets(min_num_buckets), hashtab(num_buckets, std::make_pair(EMPTY, std::make_pair(0, mapped_type()))) {} 00261 00262 explicit exhashmap(size_type nbuckets) 00263 : num_entries(0), num_buckets(internal::next_prime(nbuckets)), hashtab(num_buckets, std::make_pair(EMPTY, std::make_pair(0, mapped_type()))) {} 00264 00265 template <class InputIterator> 00266 exhashmap(InputIterator first, InputIterator last) 00267 : num_entries(0), num_buckets(min_num_buckets), hashtab(num_buckets, std::make_pair(EMPTY, std::make_pair(0, mapped_type()))) 00268 { 00269 insert(first, last); 00270 } 00271 00272 exhashmap &operator=(const exhashmap &other) 00273 { 00274 exhashmap(other).swap(*this); 00275 return *this; 00276 } 00277 00278 // Iterators 00279 iterator begin() 00280 { 00281 // Find first used bucket 00282 table_iterator bucket = hashtab.begin(); 00283 while (bucket != hashtab.end() && bucket->first != USED) 00284 ++bucket; 00285 return iterator(bucket, hashtab.end()); 00286 } 00287 00288 const_iterator begin() const 00289 { 00290 // Find first used bucket 00291 table_const_iterator bucket = hashtab.begin(); 00292 while (bucket != hashtab.end() && bucket->first != USED) 00293 ++bucket; 00294 return const_iterator(bucket, hashtab.end()); 00295 } 00296 00297 iterator end() 00298 { 00299 return iterator(hashtab.end(), hashtab.end()); 00300 } 00301 00302 const_iterator end() const 00303 { 00304 return const_iterator(hashtab.end(), hashtab.end()); 00305 } 00306 00307 // Capacity 00308 bool empty() const 00309 { 00310 return num_entries == 0; 00311 } 00312 00313 size_type size() const 00314 { 00315 return num_entries; 00316 } 00317 00318 size_type max_size() const 00319 { 00320 return hashtab.max_size(); 00321 } 00322 00323 size_type bucket_count() const 00324 { 00325 return num_buckets; 00326 } 00327 00328 // 23.3.1.2 Element access 00329 T &operator[](const key_type &x) 00330 { 00331 return insert(value_type(x, mapped_type())).first->second; 00332 } 00333 00334 // Modifiers 00335 std::pair<iterator, bool> insert(const value_type &x); 00336 00337 iterator insert(iterator pos, const value_type &x) 00338 { 00339 return insert(x).first; 00340 } 00341 00342 template <class InputIterator> 00343 void insert(InputIterator first, InputIterator last) 00344 { 00345 for (; first != last; ++first) 00346 insert(*first); 00347 } 00348 00349 void erase(iterator position) 00350 { 00351 table_iterator bucket = position.get_it_(); 00352 bucket->first = ERASED; 00353 bucket->second.first = 0; 00354 --num_entries; 00355 } 00356 00357 size_type erase(const key_type &x); 00358 00359 void swap(exhashmap &other) 00360 { 00361 hashtab.swap(other.hashtab); 00362 std::swap(num_buckets, other.num_buckets); 00363 std::swap(num_entries, other.num_entries); 00364 } 00365 00366 void clear(); 00367 00368 // Observers 00369 key_compare key_comp() const 00370 { 00371 return key_compare(); 00372 } 00373 00374 value_compare value_comp() const 00375 { 00376 return value_compare(); 00377 } 00378 00379 // 23.3.1.3 Map operations 00380 iterator find(const key_type &x); 00381 const_iterator find(const key_type &x) const; 00382 00383 size_type count(const key_type &x) const 00384 { 00385 return find(x) == end() ? 0 : 1; 00386 } 00387 00388 std::pair<iterator, iterator> equal_range(const key_type &x) 00389 { 00390 iterator i = find(x); 00391 if (i == end()) 00392 return std::make_pair(i, i); 00393 else { 00394 iterator j = ++i; 00395 return std::make_pair(i, j); 00396 } 00397 } 00398 00399 std::pair<const_iterator, const_iterator> equal_range(const key_type &x) const 00400 { 00401 const_iterator i = find(x); 00402 if (i == end()) 00403 return std::make_pair(i, i); 00404 else { 00405 const_iterator j = ++i; 00406 return std::make_pair(i, j); 00407 } 00408 } 00409 00410 friend bool operator==(const exhashmap &lhs, const exhashmap &rhs) 00411 { 00412 if (lhs.num_entries != rhs.num_entries || lhs.num_buckets != rhs.num_buckets) 00413 return false; 00414 00415 // We can't compare the tables directly as the elements may be 00416 // in different order due to the collision handling. We therefore 00417 // look up each value from the lhs map in the rhs map separately. 00418 for (const_iterator itl = lhs.begin(); itl != lhs.end(); ++itl) { 00419 const_iterator itr = rhs.find(itl->first); 00420 if (itr == rhs.end()) 00421 return false; 00422 if (itl->second != itr->second) 00423 return false; 00424 } 00425 return true; 00426 } 00427 00428 friend bool operator!=(const exhashmap &lhs, const exhashmap &rhs) 00429 { 00430 return !(lhs == rhs); 00431 } 00432 00433 #if 0 00434 void dump() const 00435 { 00436 std::clog << "num_entries = " << num_entries << std::endl; 00437 std::clog << "num_buckets = " << num_buckets << std::endl; 00438 size_type t = 0; 00439 for (table_const_iterator it = hashtab.begin(); it != hashtab.end(); ++it, ++t) { 00440 std::clog << " bucket " << t << ": "; 00441 std::clog << (it->first == EMPTY ? "free" : (it->first == USED ? "used" : "erased")) << ", " << it->second.first << " -> " << it->second.second << std::endl; 00442 } 00443 } 00444 #endif 00445 }; 00446 00448 template <typename T, template <class> class A> 00449 inline typename exhashmap<T, A>::table_iterator exhashmap<T, A>::find_bucket(const key_type &x, table_iterator tab, size_type nbuckets) 00450 { 00451 // Quadratic probing 00452 size_type h = hash_index(x, nbuckets); 00453 size_type d = 1; 00454 table_iterator it = tab + h; 00455 while (it->first != EMPTY && !(it->first == USED && key_equal()(it->second.first, x))) { 00456 h = (h + d) % nbuckets; 00457 d += 2; 00458 it = tab + h; 00459 } 00460 return it; 00461 } 00462 00464 template <typename T, template <class> class A> 00465 inline typename exhashmap<T, A>::table_const_iterator exhashmap<T, A>::find_bucket(const key_type &x, table_const_iterator tab, size_type nbuckets) 00466 { 00467 // Quadratic probing 00468 size_type h = hash_index(x, nbuckets); 00469 size_type d = 1; 00470 table_const_iterator it = tab + h; 00471 while (it->first != EMPTY && !(it->first == USED && key_equal()(it->second.first, x))) { 00472 h = (h + d) % nbuckets; 00473 d += 2; 00474 it = tab + h; 00475 } 00476 return it; 00477 } 00478 00480 template <typename T, template <class> class A> 00481 inline typename exhashmap<T, A>::table_iterator exhashmap<T, A>::find_bucket_for_insertion(const key_type &x, table_iterator tab, size_type nbuckets) 00482 { 00483 // Quadratic probing 00484 size_type h = hash_index(x, nbuckets); 00485 size_type d = 1; 00486 table_iterator it = tab + h; 00487 while (it->first != EMPTY && !key_equal()(it->second.first, x)) { 00488 h = (h + d) % nbuckets; 00489 d += 2; 00490 it = tab + h; 00491 } 00492 return it; 00493 } 00494 00496 template <typename T, template <class> class A> 00497 void exhashmap<T, A>::grow() 00498 { 00499 // Allocate new empty hash table 00500 size_type new_num_buckets = internal::next_prime(num_buckets + 1); 00501 Table new_hashtab; 00502 new_hashtab.resize(new_num_buckets); 00503 for (table_iterator it = new_hashtab.begin(); it != new_hashtab.end(); ++it) 00504 it->first = EMPTY; 00505 00506 // Re-insert all elements into new table 00507 for (table_const_iterator it = hashtab.begin(); it != hashtab.end(); ++it) { 00508 if (it->first == USED) { 00509 table_iterator bucket = find_bucket(it->second.first, new_hashtab.begin(), new_num_buckets); 00510 *bucket = *it; 00511 } 00512 } 00513 00514 // Swap with the old table 00515 hashtab.swap(new_hashtab); 00516 num_buckets = new_num_buckets; 00517 } 00518 00519 template <typename T, template <class> class A> 00520 std::pair<typename exhashmap<T, A>::iterator, bool> exhashmap<T, A>::insert(const value_type &x) 00521 { 00522 table_iterator bucket = find_bucket_for_insertion(x.first); 00523 if (bucket->first == USED) { 00524 // Value already in map 00525 return std::make_pair(iterator(bucket, hashtab.end()), false); 00526 } else { 00527 // Insert new value 00528 bucket->first = USED; 00529 bucket->second = x; 00530 ++num_entries; 00531 if (num_entries >= hwm()) { 00532 grow(); 00533 bucket = find_bucket(x.first); 00534 } 00535 return std::make_pair(iterator(bucket, hashtab.end()), true); 00536 } 00537 } 00538 00539 template <typename T, template <class> class A> 00540 typename exhashmap<T, A>::size_type exhashmap<T, A>::erase(const key_type &x) 00541 { 00542 iterator i = find(x); 00543 if (i != end()) { 00544 erase(i); 00545 return 1; 00546 } else 00547 return 0; 00548 } 00549 00550 template <typename T, template <class> class A> 00551 typename exhashmap<T, A>::iterator exhashmap<T, A>::find(const key_type &x) 00552 { 00553 table_iterator bucket = find_bucket(x); 00554 if (bucket->first == USED) 00555 return iterator(bucket, hashtab.end()); 00556 else 00557 return end(); 00558 } 00559 00560 template <typename T, template <class> class A> 00561 typename exhashmap<T, A>::const_iterator exhashmap<T, A>::find(const key_type &x) const 00562 { 00563 table_const_iterator bucket = find_bucket(x); 00564 if (bucket->first == USED) 00565 return const_iterator(bucket, hashtab.end()); 00566 else 00567 return end(); 00568 } 00569 00570 template <typename T, template <class> class A> 00571 void exhashmap<T, A>::clear() 00572 { 00573 for (table_iterator i = hashtab.begin(); i != hashtab.end(); ++i) { 00574 i->first = EMPTY; 00575 i->second.first = 0; 00576 i->second.second = mapped_type(); 00577 } 00578 num_entries = 0; 00579 } 00580 00581 } // namespace GiNaC 00582 00583 00584 // Specializations of Standard Library algorithms 00585 namespace std { 00586 00588 template <typename T, template <class> class A> 00589 inline void swap(GiNaC::exhashmap<T, A> &lhs, GiNaC::exhashmap<T, A> &rhs) 00590 { 00591 lhs.swap(rhs); 00592 } 00593 00594 } // namespace std 00595 00596 #endif // ndef GINAC_HASH_MAP_H