2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2014,2015,2017, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS 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 GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * This file declares functions for a simple hash map used by domain
40 * It is limited to integer keys and integer values. The purpose is highest
41 * efficiency and lowest memory usage possible. Thus the code is in a header,
42 * so it can be inlined where it is used.
44 * \author Berk Hess <hess@kth.se>
45 * \ingroup module_domdec
48 #ifndef GMX_DOMDEC_HASH_H
49 #define GMX_DOMDEC_HASH_H
53 #include "gromacs/mdtypes/commrec.h"
54 #include "gromacs/utility/fatalerror.h"
55 #include "gromacs/utility/smalloc.h"
57 /*! \internal \brief Hashing key-generation helper struct */
61 //! The (unique) key for storing/looking up a value
63 //! The value belonging to key
65 //! Index for the next element in the array with indentical value key%mod, -1 if there is no next element
69 /*! \internal \brief Hashing helper struct */
73 //! Keys are looked up by first checking array index key%mod in hash
75 //! mask=log2(mod), used to replace a % by the faster & operation
77 //! Allocated size of hash
79 //! The actual array containing the keys, values and next indices
81 //! The number of keys stored
83 //! Index in hash where we should start searching for space to store a new key/value
84 int start_space_search;
87 //! Clear all the entries in the hash table.
88 static void gmx_hash_clear(gmx_hash_t *hash)
92 for (i = 0; i < hash->nalloc; i++)
94 hash->hash[i].key = -1;
95 hash->hash[i].next = -1;
97 hash->start_space_search = hash->mod;
102 //! Reallocate hash table data structures.
103 static void gmx_hash_realloc(gmx_hash_t *hash, int nkey_used_estimate)
105 /* Memory requirements:
106 * nkey_used_est*(2+1-2(1-e^-1/2))*3 ints
107 * where nkey_used_est is the local number of keys used.
109 * Make the direct list twice as long as the number of local keys.
110 * The fraction of entries in the list with:
112 * >=1 size lists: 1 - e^-f
113 * where f is: the #keys / mod
114 * The fraction of keys not in the direct list is: 1-1/f(1-e^-f).
115 * The optimal table size is roughly double the number of keys.
117 /* Make the hash table a power of 2 and at least double the number of keys */
119 while (2*nkey_used_estimate > hash->mod)
123 hash->mask = hash->mod - 1;
124 hash->nalloc = over_alloc_dd(hash->mod);
125 srenew(hash->hash, hash->nalloc);
127 if (debug != nullptr)
129 fprintf(debug, "Hash table mod %d nalloc %d\n", hash->mod, hash->nalloc);
133 /*! \brief Clear all the entries in the hash table.
135 * With the current number of keys check if the table size is still
136 * good, if not optimize it with the current number of keys.
138 static void gmx_hash_clear_and_optimize(gmx_hash_t *hash)
140 /* Resize the hash table when the occupation is < 1/4 or > 2/3 */
141 if (hash->nkey > 0 &&
142 (4*hash->nkey < hash->mod || 3*hash->nkey > 2*hash->mod))
144 if (debug != nullptr)
146 fprintf(debug, "Hash table size %d #key %d: resizing\n",
147 hash->mod, hash->nkey);
149 gmx_hash_realloc(hash, hash->nkey);
152 gmx_hash_clear(hash);
155 //! Initialize hash table.
156 static gmx_hash_t *gmx_hash_init(int nkey_used_estimate)
161 hash->hash = nullptr;
163 gmx_hash_realloc(hash, nkey_used_estimate);
165 gmx_hash_clear(hash);
170 //! Set the hash entry for key to value.
171 static void gmx_hash_set(gmx_hash_t *hash, int key, int value)
173 int ind, ind_prev, i;
175 ind = key & hash->mask;
177 if (hash->hash[ind].key >= 0)
179 /* Search the last entry in the linked list for this index */
181 while (hash->hash[ind_prev].next >= 0)
183 ind_prev = hash->hash[ind_prev].next;
185 /* Search for space in the array */
186 ind = hash->start_space_search;
187 while (ind < hash->nalloc && hash->hash[ind].key >= 0)
191 /* If we are at the end of the list we need to increase the size */
192 if (ind == hash->nalloc)
194 hash->nalloc = over_alloc_dd(ind+1);
195 srenew(hash->hash, hash->nalloc);
196 for (i = ind; i < hash->nalloc; i++)
198 hash->hash[i].key = -1;
199 hash->hash[i].next = -1;
202 hash->hash[ind_prev].next = ind;
204 hash->start_space_search = ind + 1;
206 hash->hash[ind].key = key;
207 hash->hash[ind].val = value;
212 //! Delete the hash entry for key.
213 static void gmx_hash_del(gmx_hash_t *hash, int key)
218 ind = key & hash->mask;
221 if (hash->hash[ind].key == key)
225 hash->hash[ind_prev].next = hash->hash[ind].next;
227 /* This index is a linked entry, so we free an entry.
228 * Check if we are creating the first empty space.
230 if (ind < hash->start_space_search)
232 hash->start_space_search = ind;
235 hash->hash[ind].key = -1;
236 hash->hash[ind].val = -1;
237 hash->hash[ind].next = -1;
244 ind = hash->hash[ind].next;
251 //! Change the value for present hash entry for key.
252 static void gmx_hash_change_value(gmx_hash_t *hash, int key, int value)
256 ind = key & hash->mask;
259 if (hash->hash[ind].key == key)
261 hash->hash[ind].val = value;
265 ind = hash->hash[ind].next;
272 //! Change the hash value if already set, otherwise set the hash value.
273 static void gmx_hash_change_or_set(gmx_hash_t *hash, int key, int value)
277 ind = key & hash->mask;
280 if (hash->hash[ind].key == key)
282 hash->hash[ind].val = value;
286 ind = hash->hash[ind].next;
290 gmx_hash_set(hash, key, value);
295 //! Returns if the key is present, if the key is present *value is set.
296 static gmx_bool gmx_hash_get(const gmx_hash_t *hash, int key, int *value)
300 ind = key & hash->mask;
303 if (hash->hash[ind].key == key)
305 *value = hash->hash[ind].val;
309 ind = hash->hash[ind].next;
316 //! Returns the value or -1 if the key is not present.
317 static int gmx_hash_get_minone(const gmx_hash_t *hash, int key)
321 ind = key & hash->mask;
324 if (hash->hash[ind].key == key)
326 return hash->hash[ind].val;
328 ind = hash->hash[ind].next;