GiNaC  1.6.2
hash_map.h
Go to the documentation of this file.
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

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.