(git:d2a9ebd)
Loading...
Searching...
No Matches
routine_map.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
9#include "../base/base_uses.f90"
10
11 IMPLICIT NONE
12 PRIVATE
13
14
15! **************************************************************************************************
16!> \brief A hash map (also known as hashtable or dictionary).
17!> Internally the hash map uses an array to holds its data.
18!> If this array reaches a load-factor of 75%, a new array with twice the
19!> size will be allocated and the items are then copied over.
20!> This ensures that the dictionary will perform operations in O(1).
21!> \par History
22!> 12.2012 first version [Ole Schuett]
23!> 08.2019 refactored for Fypp [Ole Schuett]
24!> \author Ole Schuett
25! ***************************************************************************************************
26
27 PUBLIC :: routine_map_init
28 PUBLIC :: routine_map_items
29 PUBLIC :: routine_map_haskey
30 PUBLIC :: routine_map_set
31 PUBLIC :: routine_map_get
32 PUBLIC :: routine_map_size
33 PUBLIC :: routine_map_destroy
34 PUBLIC :: routine_map_type
35 PUBLIC :: routine_map_item_type
36
37!this is an internal type
38!Calculating hashes might be expensive, therefore they are stored
39!for use during change_capacity().
40 TYPE private_item_type
41 PRIVATE
42 CHARACTER(LEN=default_string_length) :: key = ""
43 INTEGER(kind=int_4) :: value = 0_int_4
44 INTEGER(KIND=int_8) :: hash = 0_int_8
45 TYPE(private_item_type), POINTER :: next => null()
46 END TYPE private_item_type
47
48!this is an internal type
49 TYPE private_item_p_type
50 PRIVATE
51 TYPE(private_item_type), POINTER :: p => null()
52 END TYPE private_item_p_type
53
54! this is the public type, which holds a hash map instance
56 PRIVATE
57 TYPE(private_item_p_type), DIMENSION(:), POINTER :: buckets => null()
58 INTEGER :: size = -1
59 END TYPE routine_map_type
60
61! this is a public type, its returned by routine_map_items()
63 CHARACTER(LEN=default_string_length) :: key = ""
64 INTEGER(kind=int_4) :: value = 0_int_4
66
67 CONTAINS
68
69
70! **************************************************************************************************
71!> \brief Allocates the internal data-structures of the given hash map.
72!> \param hash_map ...
73!> \param initial_capacity The initial size of the internal array (default=11).
74!> \author Ole Schuett
75! **************************************************************************************************
76 SUBROUTINE routine_map_init(hash_map, initial_capacity)
77 TYPE(routine_map_type), INTENT(inout) :: hash_map
78 INTEGER, INTENT(in), OPTIONAL :: initial_capacity
79
80 INTEGER :: initial_capacity_
81
82 IF (PRESENT(initial_capacity)) THEN
83 initial_capacity_ = initial_capacity
84 ELSE
85 initial_capacity_ = 11
86 END IF
87
88 IF (initial_capacity_ < 1) THEN
89 cpabort("initial_capacity < 1")
90 END IF
91
92 IF (ASSOCIATED(hash_map%buckets)) THEN
93 cpabort("hash map is already initialized.")
94 END IF
95
96 ALLOCATE (hash_map%buckets(initial_capacity_))
97 hash_map%size = 0
98
99 END SUBROUTINE routine_map_init
100
101! **************************************************************************************************
102!> \brief Test if the given hash map has been initialized.
103!> \param hash_map ...
104!> \return ...
105!> \author Ole Schuett
106! **************************************************************************************************
107 FUNCTION routine_map_isready(hash_map) RESULT(res)
108 TYPE(routine_map_type), INTENT(inout) :: hash_map
109 LOGICAL :: res
110 res = ASSOCIATED(hash_map%buckets)
111 END FUNCTION routine_map_isready
112
113! **************************************************************************************************
114!> \brief Deallocated the internal data-structures if the given hash map.
115!> Caution: If the stored keys or values are pointers, their targets will
116!> not get deallocated by this routine.
117!> \param hash_map ...
118!> \author Ole Schuett
119! **************************************************************************************************
120 SUBROUTINE routine_map_destroy(hash_map)
121 TYPE(routine_map_type), INTENT(inout) :: hash_map
122 TYPE(private_item_type), POINTER :: item, prev_item
123 INTEGER :: i
124
125 cpassert(ASSOCIATED(hash_map%buckets))
126
127 DO i = 1, size(hash_map%buckets)
128 item => hash_map%buckets(i)%p
129 DO WHILE (ASSOCIATED(item))
130 prev_item => item
131 item => item%next
132 DEALLOCATE (prev_item)
133 END DO
134 END DO
135
136 DEALLOCATE (hash_map%buckets)
137 hash_map%size = -1
138 END SUBROUTINE routine_map_destroy
139
140! **************************************************************************************************
141!> \brief Stores, and possibly overwrites, a given value under a given key.
142!> \param hash_map ...
143!> \param key ...
144!> \param value ...
145!> \author Ole Schuett
146! **************************************************************************************************
147 SUBROUTINE routine_map_set(hash_map, key, value)
148 TYPE(routine_map_type), INTENT(inout) :: hash_map
149 CHARACTER(LEN=default_string_length), INTENT(in) :: key
150 INTEGER(kind=int_4), INTENT(in) :: value
151 INTEGER(KIND=int_8) :: hash
152 cpassert(ASSOCIATED(hash_map%buckets))
153
154 hash = routine_map_hash_function(key)
155 CALL routine_map_set_hashed(hash_map, key, value, hash)
156 END SUBROUTINE routine_map_set
157
158! **************************************************************************************************
159!> \brief Common code used internally by routine_map_set() and routine_map_change_capacity().
160!> \param hash_map ...
161!> \param key ...
162!> \param value ...
163!> \param hash ...
164!> \author Ole Schuett
165! **************************************************************************************************
166 RECURSIVE SUBROUTINE routine_map_set_hashed(hash_map, key, value, hash)
167 TYPE(routine_map_type), INTENT(inout) :: hash_map
168 CHARACTER(LEN=default_string_length), intent(in) :: key
169 INTEGER(kind=int_4), intent(in) :: value
170 INTEGER(KIND=int_8), intent(in) :: hash
171 TYPE(private_item_type), POINTER :: item, new_item
172 INTEGER(KIND=int_8) :: idx
173
174 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
175
176 ! if already in hash map just update its value
177 item => hash_map%buckets(idx)%p
178 DO WHILE (ASSOCIATED(item))
179 IF (item%hash == hash) THEN
180 IF (routine_map_keys_equal(item%key, key)) THEN
181 item%value =value
182 RETURN
183 END IF
184 END IF
185 item => item%next
186 END DO
187
188 ! check load-factor
189 IF (4*hash_map%size > 3*size(hash_map%buckets)) THEN ! load-factor > 75%
190 call routine_map_change_capacity(hash_map, 2*size(hash_map%buckets)) !double capacity
191 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
192 END IF
193
194 ! create a new item
195 allocate (new_item)
196 new_item%hash = hash
197 new_item%key =key
198 new_item%value =value
199 new_item%next => hash_map%buckets(idx)%p
200 hash_map%buckets(idx)%p => new_item
201 hash_map%size = hash_map%size + 1
202
203 END SUBROUTINE routine_map_set_hashed
204
205! **************************************************************************************************
206!> \brief Internal routine for changing the hash map's capacity.
207!> \param hash_map ...
208!> \param new_capacity ...
209!> \author Ole Schuett
210! **************************************************************************************************
211 RECURSIVE SUBROUTINE routine_map_change_capacity(hash_map, new_capacity)
212 TYPE(routine_map_type), INTENT(inout) :: hash_map
213 INTEGER, INTENT(in) :: new_capacity
214 INTEGER :: i, old_size, new_cap
215 TYPE(private_item_type), POINTER :: item, prev_item
216 TYPE(private_item_p_type), DIMENSION(:), POINTER :: old_buckets
217 new_cap = new_capacity
218 ! pre checks
219 IF (new_cap > huge(i)) THEN
220 IF (size(hash_map%buckets) == huge(i)) RETURN ! reached maximum - stay there.
221 new_cap = huge(i) ! grow as far as possible
222 END IF
223 cpassert(new_cap >= 1)
224 cpassert(4*hash_map%size < 3*new_cap)
225
226 old_size = hash_map%size
227 old_buckets => hash_map%buckets
228 ALLOCATE (hash_map%buckets(new_capacity))
229 hash_map%size = 0
230 DO i = 1, size(old_buckets)
231 item => old_buckets(i)%p
232 DO WHILE (ASSOCIATED(item))
233 CALL routine_map_set_hashed(hash_map, item%key, item%value, item%hash)
234 prev_item => item
235 item => item%next
236 DEALLOCATE (prev_item)
237 END DO
238 END DO
239
240 DEALLOCATE (old_buckets)
241
242 cpassert(old_size == hash_map%size)
243 END SUBROUTINE routine_map_change_capacity
244
245! **************************************************************************************************
246!> \brief Gets a value for a given key from the hash map.
247!> If the key is not found the default_value will be returned.
248!> If the key is not found and default_value was not provided the program stops.
249!> \param hash_map ...
250!> \param key ...
251!> \param default_value ...
252!> \return ...
253!> \author Ole Schuett
254! **************************************************************************************************
255 FUNCTION routine_map_get(hash_map, key, default_value) RESULT(value)
256 TYPE(routine_map_type), INTENT(in) :: hash_map
257 CHARACTER(LEN=default_string_length), INTENT(in) :: key
258 INTEGER(kind=int_4), INTENT(in), OPTIONAL :: default_value
259 INTEGER(kind=int_4) :: value
260 TYPE(private_item_type), POINTER :: item
261 INTEGER(KIND=int_8) :: hash, idx
262
263 cpassert(ASSOCIATED(hash_map%buckets))
264
265 hash = routine_map_hash_function(key)
266 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
267
268 item => hash_map%buckets(idx)%p
269 DO WHILE (ASSOCIATED(item))
270 IF (item%hash == hash) THEN
271 IF (routine_map_keys_equal(item%key, key)) THEN
272 value =item%value
273 RETURN
274 END IF
275 END IF
276 item => item%next
277 END DO
278
279 IF (PRESENT(default_value)) THEN
280 value =default_value
281 RETURN
282 END IF
283
284 cpabort("Key not found.")
285 END FUNCTION routine_map_get
286
287! **************************************************************************************************
288!> \brief Remove the value for a given key from the hash map.
289!> If the key is not found the program stops.
290!> \param hash_map ...
291!> \param key ...
292!> \author Ole Schuett
293! **************************************************************************************************
294 SUBROUTINE routine_map_del(hash_map, key)
295 TYPE(routine_map_type), INTENT(inout) :: hash_map
296 CHARACTER(LEN=default_string_length), INTENT(in) :: key
297 TYPE(private_item_type), POINTER :: item, prev_item
298 INTEGER(KIND=int_8) :: hash, idx
299
300 cpassert(ASSOCIATED(hash_map%buckets))
301
302 hash = routine_map_hash_function(key)
303 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
304
305 item => hash_map%buckets(idx)%p
306 prev_item => null()
307 DO WHILE (ASSOCIATED(item))
308 IF (item%hash == hash) THEN
309 IF (routine_map_keys_equal(item%key, key)) THEN
310 IF (ASSOCIATED(prev_item)) THEN
311 prev_item%next => item%next
312 ELSE
313 hash_map%buckets(idx)%p => item%next
314 END IF
315 DEALLOCATE (item)
316 hash_map%size = hash_map%size - 1
317 RETURN
318 END IF
319 END IF
320 prev_item => item
321 item => item%next
322 END DO
323
324 cpabort("Key not found.")
325 END SUBROUTINE routine_map_del
326
327! **************************************************************************************************
328!> \brief Returns the number of key/value-items currently stored in the hash map.
329!> \param hash_map ...
330!> \return ...
331!> \author Ole Schuett
332! **************************************************************************************************
333 FUNCTION routine_map_size(hash_map) RESULT(size)
334 TYPE(routine_map_type), INTENT(IN) :: hash_map
335 INTEGER :: size
336
337 cpassert(ASSOCIATED(hash_map%buckets))
338 size = hash_map%size
339 END FUNCTION routine_map_size
340
341! **************************************************************************************************
342!> \brief Checks whether a given key is currently stored in the hash_map.
343!> \param hash_map ...
344!> \param key ...
345!> \return ...
346!> \author Ole Schuett
347! **************************************************************************************************
348 FUNCTION routine_map_haskey(hash_map, key) RESULT(res)
349 TYPE(routine_map_type), INTENT(IN) :: hash_map
350 CHARACTER(LEN=default_string_length), INTENT(IN) :: key
351 LOGICAL :: res
352 TYPE(private_item_type), POINTER :: item
353 INTEGER(KIND=int_8) :: hash, idx
354
355 cpassert(ASSOCIATED(hash_map%buckets))
356
357 res = .false.
358 IF (hash_map%size == 0) RETURN
359
360 hash = routine_map_hash_function(key)
361 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
362
363 item => hash_map%buckets(idx)%p
364 DO WHILE (ASSOCIATED(item))
365 IF (item%hash == hash) THEN
366 IF (routine_map_keys_equal(item%key, key)) THEN
367 res = .true.
368 return
369 END IF
370 END IF
371 item => item%next
372 END DO
373
374 END FUNCTION routine_map_haskey
375
376! **************************************************************************************************
377!> \brief Returns a pointer to an array of all key/value-items stored in the hash map.
378!> Caution: The caller is responsible for deallocating targeted array after usage.
379!> \param hash_map ...
380!> \return ...
381!> \author Ole Schuett
382! **************************************************************************************************
383 FUNCTION routine_map_items(hash_map) RESULT(items)
384 TYPE(routine_map_type), INTENT(IN) :: hash_map
385 TYPE(routine_map_item_type), DIMENSION(:), POINTER :: items
386
387 TYPE(private_item_type), POINTER :: item
388 INTEGER :: i, j
389
390 cpassert(ASSOCIATED(hash_map%buckets))
391
392 ALLOCATE (items(hash_map%size))
393 j = 1
394 DO i = 1, size(hash_map%buckets)
395 item => hash_map%buckets(i)%p
396 DO WHILE (ASSOCIATED(item))
397 items(j)%key =item%key
398 items(j)%value =item%value
399 j = j + 1
400 item => item%next
401 END DO
402 END DO
403
404 cpassert(j == hash_map%size + 1)
405 END FUNCTION routine_map_items
406
407! **************************************************************************************************
408!> \brief Copies all key/values-items from one hash map to another.
409!> Afterwards hash_map will contain all items from the from_hash_map and
410!> additionally all its previous items, which were not overwritten.
411!> The two hash maps have to be of the same type.
412!> \param hash_map destination of items
413!> \param from_hash_map source of items - will not be change
414!> \author Ole Schuett
415! **************************************************************************************************
416 SUBROUTINE routine_map_update(hash_map, from_hash_map)
417 TYPE(routine_map_type), INTENT(inout) :: hash_map
418 TYPE(routine_map_type), INTENT(in) :: from_hash_map
419 TYPE(routine_map_item_type), DIMENSION(:), POINTER :: from_items
420 INTEGER :: i
421
422 cpassert(ASSOCIATED(hash_map%buckets))
423 cpassert(ASSOCIATED(from_hash_map%buckets))
424
425 from_items => routine_map_items(from_hash_map)
426 DO i = 1, size(from_items)
427 CALL routine_map_set(hash_map, from_items(i)%key, from_items(i)%value)
428 END DO
429 DEALLOCATE (from_items)
430 END SUBROUTINE routine_map_update
431
432
433! **************************************************************************************************
434! This is joaat_hash from string_table.F
435!
436!> \brief generates the hash of a given string
437!> \param key a string of any length
438!> \return ...
439!> \par History
440!> 09.2006 created [Joost VandeVondele]
441!> 12.2012 copied and adopted [ole]
442!> \note
443!> http://en.wikipedia.org/wiki/Hash_table
444!> http://www.burtleburtle.net/bob/hash/doobs.html
445! **************************************************************************************************
446 PURE FUNCTION routine_map_hash_function(key) RESULT(hash)
447 CHARACTER(LEN=*), INTENT(IN) :: key
448 INTEGER(KIND=int_8) :: hash
449
450 INTEGER(KIND=int_8), PARAMETER :: b32 = 2_int_8**32 - 1_int_8
451
452 INTEGER :: i
453
454 hash = 0_int_8
455 DO i = 1, len(key)
456 hash = iand(hash + ichar(key(i:i)), b32)
457 hash = iand(hash + iand(ishft(hash, 10), b32), b32)
458 hash = iand(ieor(hash, iand(ishft(hash, -6), b32)), b32)
459 END DO
460 hash = iand(hash + iand(ishft(hash, 3), b32), b32)
461 hash = iand(ieor(hash, iand(ishft(hash, -11), b32)), b32)
462 hash = iand(hash + iand(ishft(hash, 15), b32), b32)
463 END FUNCTION routine_map_hash_function
464
465! **************************************************************************************************
466!> \brief ...
467!> \param key ...
468!> \return ...
469! **************************************************************************************************
470 PURE FUNCTION routine_map_keys_equal(key1, key2) RESULT(res)
471 CHARACTER(LEN=*), INTENT(IN) :: key1, key2
472 LOGICAL :: res
473
474 res = (key1 == key2)
475 END FUNCTION routine_map_keys_equal
476
477END MODULE routine_map
static unsigned int hash(const dbm_task_t task)
Private hash function based on Szudzik's elegant pairing. Using unsigned int to return a positive num...
static GRID_HOST_DEVICE int idx(const orbital a)
Return coset index of given orbital angular momentum.
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public int_8
Definition kinds.F:54
integer, parameter, public default_string_length
Definition kinds.F:57
integer, parameter, public int_4
Definition kinds.F:51
integer(kind=int_4) function, public routine_map_get(hash_map, key, default_value)
Gets a value for a given key from the hash map. If the key is not found the default_value will be ret...
integer function, public routine_map_size(hash_map)
Returns the number of key/value-items currently stored in the hash map.
subroutine, public routine_map_init(hash_map, initial_capacity)
Allocates the internal data-structures of the given hash map.
Definition routine_map.F:77
logical function, public routine_map_haskey(hash_map, key)
Checks whether a given key is currently stored in the hash_map.
type(routine_map_item_type) function, dimension(:), pointer, public routine_map_items(hash_map)
Returns a pointer to an array of all key/value-items stored in the hash map. Caution: The caller is r...
subroutine, public routine_map_destroy(hash_map)
Deallocated the internal data-structures if the given hash map. Caution: If the stored keys or values...
subroutine, public routine_map_set(hash_map, key, value)
Stores, and possibly overwrites, a given value under a given key.