added exhashmap<> as a replacement for map<> that uses hashing
[ginac.git] / ginac / hash_map.h
1 /** @file hash_map.h
2  *
3  *  Replacement for map<> using hash tables. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2003 Johannes Gutenberg University Mainz, Germany
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #ifndef __GINAC_HASH_MAP_H__
24 #define __GINAC_HASH_MAP_H__
25
26 #include <list>
27 #include <iterator>
28 #include <algorithm>
29 #include <functional>
30 #include <utility>
31
32
33 namespace GiNaC {
34
35 /*
36  *  "Hashmap Light" - buckets only contain one value, quadratic probing,
37  *  grows automatically
38  */
39
40 namespace internal {
41
42 // List of prime numbers shamelessly stolen from GCC STL
43 enum { num_primes = 29 };
44
45 static const unsigned long prime_list[num_primes] =
46 {
47         31ul,        53ul,         97ul,         193ul,       389ul,
48         769ul,       1543ul,       3079ul,       6151ul,      12289ul,
49         24593ul,     49157ul,      98317ul,      196613ul,    393241ul,
50         786433ul,    1572869ul,    3145739ul,    6291469ul,   12582917ul,
51         25165843ul,  50331653ul,   100663319ul,  201326611ul, 402653189ul,
52         805306457ul, 1610612741ul, 3221225473ul, 4294967291ul
53 };
54
55 inline unsigned long next_prime(unsigned long n)
56 {
57         const unsigned long *first = prime_list;
58         const unsigned long *last = prime_list + num_primes;
59         const unsigned long *pos = std::lower_bound(first, last, n);
60         return pos == last ? *(last - 1) : *pos;
61 }
62
63 } // namespace internal
64
65
66 // Define default arguments
67 template <typename T, template <class> class A = std::allocator>
68 class exhashmap;
69
70
71 /** Pair Associative Container with 'ex' objects as keys, that is implemented
72  *  with a hash table and can be used as a replacement for map<> in many cases.
73  *
74  *  Differences to map<>:
75  *   - no lower_bound()/upper_bound()
76  *   - no "insert with a hint" insert(iterator, key_type)
77  *   - no reverse iterators, no rbegin()/rend()
78  *   - no operator<()
79  *   - comparison functor is hardcoded to ex_is_less
80  *   - bucket_count() returns the number of buckets allocated in the hash table
81  *   - insert() and erase() invalidate all iterators
82  *   - average complexity of find() is constant time, worst case is O(n) */
83 template <typename T, template <class> class A>
84 class exhashmap {
85 public:
86         static const unsigned min_num_buckets = 5; // must be prime
87
88         // Standard types
89         typedef ex key_type;
90         typedef T mapped_type;
91         typedef std::pair<key_type, T> value_type;
92         typedef ex_is_less key_compare;
93         typedef ex_is_equal key_equal;
94         typedef value_type & reference;
95         typedef const value_type & const_reference;
96         typedef value_type * pointer;
97         typedef const value_type * const_pointer;
98
99 protected:
100         // Private types
101         enum bucket_state {
102                 EMPTY,  ///< bucket empty (never used)
103                 USED,   ///< bucket in use
104                 ERASED  ///< bucket empty (element deleted), but may be part of a search chain
105         };
106         typedef std::pair<bucket_state, value_type> Bucket;
107
108 public:
109         // More standard types
110         typedef A<Bucket> allocator_type;
111
112 protected:
113         // More private types
114         typedef std::vector<Bucket, allocator_type> Table;
115
116         typedef typename Table::iterator table_iterator;
117         typedef typename Table::const_iterator table_const_iterator;
118
119 public:
120         // Iterators
121         template <typename Pointer, typename Reference, class TableIterator>
122         class exhashmap_iterator : public std::iterator<std::forward_iterator_tag, value_type, typename Table::difference_type, Pointer, Reference> {
123         protected:
124                 friend class exhashmap;
125
126         public:
127                 exhashmap_iterator() {}
128                 exhashmap_iterator(TableIterator t, TableIterator te)
129                  : it(t), table_end(te) {}
130
131                 // Allow iterator to const_iterator conversion
132                 template <typename P, typename R, class TI>
133                 exhashmap_iterator(const exhashmap_iterator<P, R, TI> &other)
134                  : it(other.get_it_()), table_end(other.get_table_end_()) {}
135
136                 typename exhashmap_iterator::reference operator*() const
137                 {
138                         return it->second;
139                 }
140
141                 typename exhashmap_iterator::pointer operator->() const
142                 {
143                         return &(it->second);
144                 }
145
146                 exhashmap_iterator &operator++()
147                 {
148                         increment();
149                         return *this;
150                 }
151
152                 exhashmap_iterator operator++(int)
153                 {
154                         exhashmap_iterator tmp = *this;
155                         increment();
156                         return tmp;
157                 }
158
159                 template <typename P, typename R, class TI>
160                 bool operator==(const exhashmap_iterator<P, R, TI> &other) const
161                 {
162                         return it == other.get_it_();
163                 }
164
165                 template <typename P, typename R, class TI>
166                 bool operator!=(const exhashmap_iterator<P, R, TI> &other) const
167                 {
168                         return it != other.get_it_();
169                 }
170
171                 // Private access function
172                 TableIterator get_it_() const { return it; }
173                 TableIterator get_table_end_() const { return table_end; }
174
175         protected:
176                 TableIterator it;        ///< Pointer to current bucket
177                 TableIterator table_end; ///< Pointer to one-past-last bucket
178
179                 void increment()
180                 {
181                         if (it != table_end)
182                                 ++it;
183
184                         // Skip empty and erased buckets
185                         while (it != table_end && it->first != USED)
186                                 ++it;
187                 }
188         };
189
190         typedef exhashmap_iterator<value_type*, value_type&, table_iterator> iterator;
191         typedef exhashmap_iterator<const value_type*, const value_type&, table_const_iterator> const_iterator;
192
193         // More standard types
194         typedef typename Table::size_type size_type;
195         typedef typename Table::difference_type difference_type;
196
197         class value_compare : public std::binary_function<value_type, value_type, bool>, private key_compare {
198                 friend class exhashmap;
199         public:
200                 bool operator()(const value_type &lhs, const value_type &rhs) const
201                 {
202                         return key_compare::operator()(lhs.first, rhs.first);
203                 }
204
205                 bool operator()(const key_type &lhs, const value_type &rhs) const
206                 {
207                         return key_compare::operator()(lhs, rhs.first);
208                 }
209
210                 bool operator()(const value_type &lhs, const key_type &rhs) const
211                 {
212                         return key_compare::operator()(lhs.first, rhs);
213                 }
214         };
215
216 protected:
217         // Private data
218         Table hashtab;         ///< Vector of buckets, each bucket is kept sorted
219         size_type num_buckets; ///< Number of buckets (= hashtab.size())
220         size_type num_entries; ///< Number of values stored in container (cached for faster operation of size())
221
222         /** Return index of key in hash table. */
223         static size_type hash_index(const key_type &x, size_type nbuckets)
224         {
225                 return x.gethash() % nbuckets;
226         }
227
228         static table_iterator find_bucket(const key_type &x, table_iterator tab, size_type nbuckets);
229         static table_const_iterator find_bucket(const key_type &x, table_const_iterator tab, size_type nbuckets);
230         static table_iterator find_bucket_for_insertion(const key_type &x, table_iterator tab, size_type nbuckets);
231
232         /** Return pointer to bucket corresponding to key (or first empty bucket). */
233         table_iterator find_bucket(const key_type &x)
234         {
235                 return find_bucket(x, hashtab.begin(), num_buckets);
236         }
237
238         /** Return pointer to bucket corresponding to key (or first empty bucket). */
239         table_const_iterator find_bucket(const key_type &x) const
240         {
241                 return find_bucket(x, hashtab.begin(), num_buckets);
242         }
243
244         /** Return pointer to bucket corresponding to key (or first empty or erased bucket). */
245         table_iterator find_bucket_for_insertion(const key_type &x)
246         {
247                 return find_bucket_for_insertion(x, hashtab.begin(), num_buckets);
248         }
249
250         /** Return number of entries above which the table will grow. */
251         size_type hwm() const
252         {
253                 // Try to keep at least 25% of the buckets free
254                 return num_buckets - (num_buckets >> 2);
255         }
256
257         /** Empty all buckets in the table. */
258         void empty_all_buckets()
259         {
260                 for (table_iterator i = hashtab.begin(); i != hashtab.end(); ++i)
261                         i->first = EMPTY;
262         }
263
264         void grow();
265
266 public:
267         // 23.3.1.1 Construct/copy/destroy
268         exhashmap() : num_buckets(min_num_buckets), num_entries(0)
269         {
270                 hashtab.resize(num_buckets);
271                 empty_all_buckets();
272         }
273
274         explicit exhashmap(size_type nbuckets) : num_entries(0)
275         {
276                 num_buckets = internal::next_prime(nbuckets);
277                 hashtab.resize(num_buckets);
278                 empty_all_buckets();
279         }
280
281         template <class InputIterator>
282         exhashmap(InputIterator first, InputIterator last) : num_buckets(min_num_buckets), num_entries(0)
283         {
284                 hashtab.resize(num_buckets);
285                 empty_all_buckets();
286                 insert(first, last);
287         }
288
289         exhashmap &operator=(const exhashmap &other)
290         {
291                 exhashmap(other).swap(*this);
292                 return *this;
293         }
294
295         // Iterators
296         iterator begin()
297         {
298                 // Find first used bucket
299                 table_iterator bucket = hashtab.begin();
300                 while (bucket != hashtab.end() && bucket->first != USED)
301                         ++bucket;
302                 return iterator(bucket, hashtab.end());
303         }
304
305         const_iterator begin() const
306         {
307                 // Find first used bucket
308                 table_const_iterator bucket = hashtab.begin();
309                 while (bucket != hashtab.end() && bucket->first != USED)
310                         ++bucket;
311                 return const_iterator(bucket, hashtab.end());
312         }
313
314         iterator end()
315         {
316                 return iterator(hashtab.end(), hashtab.end());
317         }
318
319         const_iterator end() const
320         {
321                 return const_iterator(hashtab.end(), hashtab.end());
322         }
323
324         // Capacity
325         bool empty() const
326         {
327                 return num_entries == 0;
328         }
329
330         size_type size() const
331         {
332                 return num_entries;
333         }
334
335         size_type max_size() const
336         {
337                 return hashtab.max_size();
338         }
339
340         size_type bucket_count() const
341         {
342                 return num_buckets;
343         }
344
345         // 23.3.1.2 Element access
346         T &operator[](const key_type &x)
347         {
348                 return insert(value_type(x, mapped_type())).first->second;
349         }
350
351         // Modifiers
352         std::pair<iterator, bool> insert(const value_type &x);
353
354         template <class InputIterator>
355         void insert(InputIterator first, InputIterator last)
356         {
357                 for (; first != last; ++first)
358                         insert(*first);
359         }
360
361         void erase(iterator position)
362         {
363                 table_iterator bucket = position.get_it_();
364                 bucket->first = ERASED;
365                 bucket->second.first = 0;
366                 --num_entries;
367         }
368
369         size_type erase(const key_type &x);
370
371         void swap(exhashmap &other)
372         {
373                 hashtab.swap(other.hashtab);
374                 std::swap(num_buckets, other.num_buckets);
375                 std::swap(num_entries, other.num_entries);
376         }
377
378         void clear();
379
380         // Observers
381         key_compare key_comp() const
382         {
383                 return key_compare();
384         }
385
386         value_compare value_comp() const
387         {
388                 return value_compare();
389         }
390
391         // 23.3.1.3 Map operations
392         iterator find(const key_type &x);
393         const_iterator find(const key_type &x) const;
394
395         size_type count(const key_type &x) const
396         {
397                 return find(x) == end() ? 0 : 1;
398         }
399
400         std::pair<iterator, iterator> equal_range(const key_type &x)
401         {
402                 iterator i = find(x);
403                 if (i == end())
404                         return std::make_pair(i, i);
405                 else {
406                         iterator j = ++i;
407                         return std::make_pair(i, j);
408                 }
409         }
410
411         std::pair<const_iterator, const_iterator> equal_range(const key_type &x) const
412         {
413                 const_iterator i = find(x);
414                 if (i == end())
415                         return std::make_pair(i, i);
416                 else {
417                         const_iterator j = ++i;
418                         return std::make_pair(i, j);
419                 }
420         }
421
422         friend bool operator==(const exhashmap &lhs, const exhashmap &rhs)
423         {
424                 if (lhs.num_entries != rhs.num_entries || lhs.num_buckets != rhs.num_buckets)
425                         return false;
426
427                 // We can't compare the tables directly as the elements may be
428                 // in different order due to the collision handling. We therefore
429                 // look up each value from the lhs map in the rhs map separately.
430                 for (const_iterator itl = lhs.begin(); itl != lhs.end(); ++itl) {
431                         const_iterator itr = rhs.find(itl->first);
432                         if (itr == rhs.end())
433                                 return false;
434                         if (itl->second != itr->second)
435                                 return false;
436                 }
437                 return true;
438         }
439
440         friend bool operator!=(const exhashmap &lhs, const exhashmap &rhs)
441         {
442                 return !(lhs == rhs);
443         }
444
445         void dump() const
446         {
447                 std::clog << "num_entries = " << num_entries << std::endl;
448                 std::clog << "num_buckets = " << num_buckets << std::endl;
449                 size_type t = 0;
450                 for (table_const_iterator it = hashtab.begin(); it != hashtab.end(); ++it, ++t) {
451                         std::clog << " bucket " << t << ": ";
452                         std::clog << (it->first == EMPTY ? "free" : (it->first == USED ? "used" : "erased")) << ", " << it->second.first << " -> " << it->second.second << std::endl;
453                 }
454         }
455 };
456
457 /** Return pointer to bucket corresponding to key (or first empty bucket). */
458 template <typename T, template <class> class A>
459 inline typename exhashmap<T, A>::table_iterator exhashmap<T, A>::find_bucket(const key_type &x, table_iterator tab, size_type nbuckets)
460 {
461         // Quadratic probing
462         size_type h = hash_index(x, nbuckets);
463         size_type d = 1;
464         table_iterator it = tab + h;
465         while (it->first != EMPTY && !(it->first == USED && key_equal()(it->second.first, x))) {
466                 h = (h + d) % nbuckets;
467                 d += 2;
468                 it = tab + h;
469         }
470         return it;
471 }
472
473 /** Return pointer to bucket corresponding to key (or first empty bucket). */
474 template <typename T, template <class> class A>
475 inline typename exhashmap<T, A>::table_const_iterator exhashmap<T, A>::find_bucket(const key_type &x, table_const_iterator tab, size_type nbuckets)
476 {
477         // Quadratic probing
478         size_type h = hash_index(x, nbuckets);
479         size_type d = 1;
480         table_const_iterator it = tab + h;
481         while (it->first != EMPTY && !(it->first == USED && key_equal()(it->second.first, x))) {
482                 h = (h + d) % nbuckets;
483                 d += 2;
484                 it = tab + h;
485         }
486         return it;
487 }
488
489 /** Return pointer to bucket corresponding to key (or first empty or erased bucket). */
490 template <typename T, template <class> class A>
491 inline typename exhashmap<T, A>::table_iterator exhashmap<T, A>::find_bucket_for_insertion(const key_type &x, table_iterator tab, size_type nbuckets)
492 {
493         // Quadratic probing
494         size_type h = hash_index(x, nbuckets);
495         size_type d = 1;
496         table_iterator it = tab + h;
497         while (it->first != EMPTY && !key_equal()(it->second.first, x)) {
498                 h = (h + d) % nbuckets;
499                 d += 2;
500                 it = tab + h;
501         }
502         return it;
503 }
504
505 /** Grow hash table */
506 template <typename T, template <class> class A>
507 void exhashmap<T, A>::grow()
508 {
509         // Allocate new empty hash table
510         size_type new_num_buckets = internal::next_prime(num_buckets + 1);
511         Table new_hashtab;
512         new_hashtab.resize(new_num_buckets);
513         for (table_iterator it = new_hashtab.begin(); it != new_hashtab.end(); ++it)
514                 it->first = EMPTY;
515
516         // Re-insert all elements into new table
517         for (table_const_iterator it = hashtab.begin(); it != hashtab.end(); ++it) {
518                 if (it->first == USED) {
519                         table_iterator bucket = find_bucket(it->second.first, new_hashtab.begin(), new_num_buckets);
520                         *bucket = *it;
521                 }
522         }
523
524         // Swap with the old table
525         hashtab.swap(new_hashtab);
526         num_buckets = new_num_buckets;
527 }
528
529 template <typename T, template <class> class A>
530 std::pair<typename exhashmap<T, A>::iterator, bool> exhashmap<T, A>::insert(const value_type &x)
531 {
532         table_iterator bucket = find_bucket_for_insertion(x.first);
533         if (bucket->first == USED) {
534                 // Value already in map
535                 return std::make_pair(iterator(bucket, hashtab.end()), false);
536         } else {
537                 // Insert new value
538                 bucket->first = USED;
539                 bucket->second = x;
540                 ++num_entries;
541                 if (num_entries >= hwm()) {
542                         grow();
543                         bucket = find_bucket(x.first);
544                 }
545                 return std::make_pair(iterator(bucket, hashtab.end()), true);
546         }
547 }
548
549 template <typename T, template <class> class A>
550 typename exhashmap<T, A>::size_type exhashmap<T, A>::erase(const key_type &x)
551 {
552         iterator i = find(x);
553         if (i != end()) {
554                 erase(i);
555                 return 1;
556         } else
557                 return 0;
558 }
559
560 template <typename T, template <class> class A>
561 typename exhashmap<T, A>::iterator exhashmap<T, A>::find(const key_type &x)
562 {
563         table_iterator bucket = find_bucket(x);
564         if (bucket->first == USED)
565                 return iterator(bucket, hashtab.end());
566         else
567                 return end();
568 }
569
570 template <typename T, template <class> class A>
571 typename exhashmap<T, A>::const_iterator exhashmap<T, A>::find(const key_type &x) const
572 {
573         table_const_iterator bucket = find_bucket(x);
574         if (bucket->first == USED)
575                 return const_iterator(bucket, hashtab.end());
576         else
577                 return end();
578 }
579
580 template <typename T, template <class> class A>
581 void exhashmap<T, A>::clear()
582 {
583         for (table_iterator i = hashtab.begin(); i != hashtab.end(); ++i) {
584                 i->first = EMPTY;
585                 i->second.first = 0;
586                 i->second.second = T();
587         }
588         num_entries = 0;
589 }
590
591 } // namespace GiNaC
592
593
594 // Specializations of Standard Library algorithms
595 namespace std {
596
597 /** Specialization of std::swap() for exhashmap. */
598 template <typename T, template <class> class A>
599 inline void swap(GiNaC::exhashmap<T, A> &lhs, GiNaC::exhashmap<T, A> &rhs)
600 {
601         lhs.swap(rhs);
602 }
603
604 } // namespace std
605
606 #endif // ndef __GINAC_HASH_MAP_H__