(git:374b731)
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-2024 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) &
89 cpabort("initial_capacity < 1")
90
91 IF (ASSOCIATED(hash_map%buckets)) &
92 cpabort("hash map is already initialized.")
93
94 ALLOCATE (hash_map%buckets(initial_capacity_))
95 hash_map%size = 0
96
97 END SUBROUTINE routine_map_init
98
99! **************************************************************************************************
100!> \brief Test if the given hash map has been initialized.
101!> \param hash_map ...
102!> \return ...
103!> \author Ole Schuett
104! **************************************************************************************************
105 FUNCTION routine_map_isready(hash_map) RESULT(res)
106 TYPE(routine_map_type), INTENT(inout) :: hash_map
107 LOGICAL :: res
108 res = ASSOCIATED(hash_map%buckets)
109 END FUNCTION routine_map_isready
110
111! **************************************************************************************************
112!> \brief Deallocated the internal data-structures if the given hash map.
113!> Caution: If the stored keys or values are pointers, their targets will
114!> not get deallocated by this routine.
115!> \param hash_map ...
116!> \author Ole Schuett
117! **************************************************************************************************
118 SUBROUTINE routine_map_destroy(hash_map)
119 TYPE(routine_map_type), INTENT(inout) :: hash_map
120 TYPE(private_item_type), POINTER :: item, prev_item
121 INTEGER :: i
122
123 cpassert(ASSOCIATED(hash_map%buckets))
124
125 DO i = 1, size(hash_map%buckets)
126 item => hash_map%buckets(i)%p
127 DO WHILE (ASSOCIATED(item))
128 prev_item => item
129 item => item%next
130 DEALLOCATE (prev_item)
131 END DO
132 END DO
133
134 DEALLOCATE (hash_map%buckets)
135 hash_map%size = -1
136 END SUBROUTINE routine_map_destroy
137
138! **************************************************************************************************
139!> \brief Stores, and possibly overwrites, a given value under a given key.
140!> \param hash_map ...
141!> \param key ...
142!> \param value ...
143!> \author Ole Schuett
144! **************************************************************************************************
145 SUBROUTINE routine_map_set(hash_map, key, value)
146 TYPE(routine_map_type), INTENT(inout) :: hash_map
147 CHARACTER(LEN=default_string_length), INTENT(in) :: key
148 INTEGER(kind=int_4), INTENT(in) :: value
149 INTEGER(KIND=int_8) :: hash
150 cpassert(ASSOCIATED(hash_map%buckets))
151
152 hash = routine_map_hash_function(key)
153 CALL routine_map_set_hashed(hash_map, key, value, hash)
154 END SUBROUTINE routine_map_set
155
156! **************************************************************************************************
157!> \brief Common code used internally by routine_map_set() and routine_map_change_capacity().
158!> \param hash_map ...
159!> \param key ...
160!> \param value ...
161!> \param hash ...
162!> \author Ole Schuett
163! **************************************************************************************************
164 RECURSIVE SUBROUTINE routine_map_set_hashed(hash_map, key, value, hash)
165 TYPE(routine_map_type), INTENT(inout) :: hash_map
166 CHARACTER(LEN=default_string_length), intent(in) :: key
167 INTEGER(kind=int_4), intent(in) :: value
168 INTEGER(KIND=int_8), intent(in) :: hash
169 TYPE(private_item_type), POINTER :: item, new_item
170 INTEGER(KIND=int_8) :: idx
171
172 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
173
174 ! if already in hash map just update its value
175 item => hash_map%buckets(idx)%p
176 DO WHILE (ASSOCIATED(item))
177 IF (item%hash == hash) THEN
178 IF (routine_map_keys_equal(item%key, key)) THEN
179 item%value =value
180 RETURN
181 END IF
182 END IF
183 item => item%next
184 END DO
185
186 ! check load-factor
187 IF (4*hash_map%size > 3*size(hash_map%buckets)) THEN ! load-factor > 75%
188 call routine_map_change_capacity(hash_map, 2*size(hash_map%buckets)) !double capacity
189 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
190 END IF
191
192 ! create a new item
193 allocate (new_item)
194 new_item%hash = hash
195 new_item%key =key
196 new_item%value =value
197 new_item%next => hash_map%buckets(idx)%p
198 hash_map%buckets(idx)%p => new_item
199 hash_map%size = hash_map%size + 1
200
201 END SUBROUTINE routine_map_set_hashed
202
203! **************************************************************************************************
204!> \brief Internal routine for changing the hash map's capacity.
205!> \param hash_map ...
206!> \param new_capacity ...
207!> \author Ole Schuett
208! **************************************************************************************************
209 RECURSIVE SUBROUTINE routine_map_change_capacity(hash_map, new_capacity)
210 TYPE(routine_map_type), INTENT(inout) :: hash_map
211 INTEGER, INTENT(in) :: new_capacity
212 INTEGER :: i, old_size, new_cap
213 TYPE(private_item_type), POINTER :: item, prev_item
214 TYPE(private_item_p_type), DIMENSION(:), POINTER :: old_buckets
215 new_cap = new_capacity
216 ! pre checks
217 IF (new_cap > huge(i)) THEN
218 IF (size(hash_map%buckets) == huge(i)) RETURN ! reached maximum - stay there.
219 new_cap = huge(i) ! grow as far as possible
220 END IF
221 cpassert(new_cap >= 1)
222 cpassert(4*hash_map%size < 3*new_cap)
223
224 old_size = hash_map%size
225 old_buckets => hash_map%buckets
226 ALLOCATE (hash_map%buckets(new_capacity))
227 hash_map%size = 0
228 DO i = 1, size(old_buckets)
229 item => old_buckets(i)%p
230 DO WHILE (ASSOCIATED(item))
231 CALL routine_map_set_hashed(hash_map, item%key, item%value, item%hash)
232 prev_item => item
233 item => item%next
234 DEALLOCATE (prev_item)
235 END DO
236 END DO
237
238 DEALLOCATE (old_buckets)
239
240 cpassert(old_size == hash_map%size)
241 END SUBROUTINE routine_map_change_capacity
242
243! **************************************************************************************************
244!> \brief Gets a value for a given key from the hash map.
245!> If the key is not found the default_value will be returned.
246!> If the key is not found and default_value was not provided the program stops.
247!> \param hash_map ...
248!> \param key ...
249!> \param default_value ...
250!> \return ...
251!> \author Ole Schuett
252! **************************************************************************************************
253 FUNCTION routine_map_get(hash_map, key, default_value) RESULT(value)
254 TYPE(routine_map_type), INTENT(in) :: hash_map
255 CHARACTER(LEN=default_string_length), INTENT(in) :: key
256 INTEGER(kind=int_4), INTENT(in), OPTIONAL :: default_value
257 INTEGER(kind=int_4) :: value
258 TYPE(private_item_type), POINTER :: item
259 INTEGER(KIND=int_8) :: hash, idx
260
261 cpassert(ASSOCIATED(hash_map%buckets))
262
263 hash = routine_map_hash_function(key)
264 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
265
266 item => hash_map%buckets(idx)%p
267 DO WHILE (ASSOCIATED(item))
268 IF (item%hash == hash) THEN
269 IF (routine_map_keys_equal(item%key, key)) THEN
270 value =item%value
271 RETURN
272 END IF
273 END IF
274 item => item%next
275 END DO
276
277 IF (PRESENT(default_value)) THEN
278 value =default_value
279 RETURN
280 END IF
281
282 cpabort("Key not found.")
283 END FUNCTION routine_map_get
284
285! **************************************************************************************************
286!> \brief Remove the value for a given key from the hash map.
287!> If the key is not found the program stops.
288!> \param hash_map ...
289!> \param key ...
290!> \author Ole Schuett
291! **************************************************************************************************
292 SUBROUTINE routine_map_del(hash_map, key)
293 TYPE(routine_map_type), INTENT(inout) :: hash_map
294 CHARACTER(LEN=default_string_length), INTENT(in) :: key
295 TYPE(private_item_type), POINTER :: item, prev_item
296 INTEGER(KIND=int_8) :: hash, idx
297
298 cpassert(ASSOCIATED(hash_map%buckets))
299
300 hash = routine_map_hash_function(key)
301 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
302
303 item => hash_map%buckets(idx)%p
304 prev_item => null()
305 DO WHILE (ASSOCIATED(item))
306 IF (item%hash == hash) THEN
307 IF (routine_map_keys_equal(item%key, key)) THEN
308 IF (ASSOCIATED(prev_item)) THEN
309 prev_item%next => item%next
310 ELSE
311 hash_map%buckets(idx)%p => item%next
312 END IF
313 DEALLOCATE (item)
314 hash_map%size = hash_map%size - 1
315 RETURN
316 END IF
317 END IF
318 prev_item => item
319 item => item%next
320 END DO
321
322 cpabort("Key not found.")
323 END SUBROUTINE routine_map_del
324
325! **************************************************************************************************
326!> \brief Returns the number of key/value-items currently stored in the hash map.
327!> \param hash_map ...
328!> \return ...
329!> \author Ole Schuett
330! **************************************************************************************************
331 FUNCTION routine_map_size(hash_map) RESULT(size)
332 TYPE(routine_map_type), INTENT(IN) :: hash_map
333 INTEGER :: size
334
335 cpassert(ASSOCIATED(hash_map%buckets))
336 size = hash_map%size
337 END FUNCTION routine_map_size
338
339! **************************************************************************************************
340!> \brief Checks whether a given key is currently stored in the hash_map.
341!> \param hash_map ...
342!> \param key ...
343!> \return ...
344!> \author Ole Schuett
345! **************************************************************************************************
346 FUNCTION routine_map_haskey(hash_map, key) RESULT(res)
347 TYPE(routine_map_type), INTENT(IN) :: hash_map
348 CHARACTER(LEN=default_string_length), INTENT(IN) :: key
349 LOGICAL :: res
350 TYPE(private_item_type), POINTER :: item
351 INTEGER(KIND=int_8) :: hash, idx
352
353 cpassert(ASSOCIATED(hash_map%buckets))
354
355 res = .false.
356 IF (hash_map%size == 0) RETURN
357
358 hash = routine_map_hash_function(key)
359 idx = mod(hash, int(size(hash_map%buckets), kind=int_8)) + 1
360
361 item => hash_map%buckets(idx)%p
362 DO WHILE (ASSOCIATED(item))
363 IF (item%hash == hash) THEN
364 IF (routine_map_keys_equal(item%key, key)) THEN
365 res = .true.
366 return
367 END IF
368 END IF
369 item => item%next
370 END DO
371
372 END FUNCTION routine_map_haskey
373
374! **************************************************************************************************
375!> \brief Returns a pointer to an array of all key/value-items stored in the hash map.
376!> Caution: The caller is responsible for deallocating targeted array after usage.
377!> \param hash_map ...
378!> \return ...
379!> \author Ole Schuett
380! **************************************************************************************************
381 FUNCTION routine_map_items(hash_map) RESULT(items)
382 TYPE(routine_map_type), INTENT(IN) :: hash_map
383 TYPE(routine_map_item_type), DIMENSION(:), POINTER :: items
384
385 TYPE(private_item_type), POINTER :: item
386 INTEGER :: i, j
387
388 cpassert(ASSOCIATED(hash_map%buckets))
389
390 ALLOCATE (items(hash_map%size))
391 j = 1
392 DO i = 1, size(hash_map%buckets)
393 item => hash_map%buckets(i)%p
394 DO WHILE (ASSOCIATED(item))
395 items(j)%key =item%key
396 items(j)%value =item%value
397 j = j + 1
398 item => item%next
399 END DO
400 END DO
401
402 cpassert(j == hash_map%size + 1)
403 END FUNCTION routine_map_items
404
405! **************************************************************************************************
406!> \brief Copies all key/values-items from one hash map to another.
407!> Afterwards hash_map will contain all items from the from_hash_map and
408!> additionally all its previous items, which were not overwritten.
409!> The two hash maps have to be of the same type.
410!> \param hash_map destination of items
411!> \param from_hash_map source of items - will not be change
412!> \author Ole Schuett
413! **************************************************************************************************
414 SUBROUTINE routine_map_update(hash_map, from_hash_map)
415 TYPE(routine_map_type), INTENT(inout) :: hash_map
416 TYPE(routine_map_type), INTENT(in) :: from_hash_map
417 TYPE(routine_map_item_type), DIMENSION(:), POINTER :: from_items
418 INTEGER :: i
419
420 cpassert(ASSOCIATED(hash_map%buckets))
421 cpassert(ASSOCIATED(from_hash_map%buckets))
422
423 from_items => routine_map_items(from_hash_map)
424 DO i = 1, size(from_items)
425 CALL routine_map_set(hash_map, from_items(i)%key, from_items(i)%value)
426 END DO
427 DEALLOCATE (from_items)
428 END SUBROUTINE routine_map_update
429
430
431! **************************************************************************************************
432! This is joaat_hash from string_table.F
433!
434!> \brief generates the hash of a given string
435!> \param key a string of any length
436!> \return ...
437!> \par History
438!> 09.2006 created [Joost VandeVondele]
439!> 12.2012 copied and adopted [ole]
440!> \note
441!> http://en.wikipedia.org/wiki/Hash_table
442!> http://www.burtleburtle.net/bob/hash/doobs.html
443! **************************************************************************************************
444 PURE FUNCTION routine_map_hash_function(key) RESULT(hash)
445 CHARACTER(LEN=*), INTENT(IN) :: key
446 INTEGER(KIND=int_8) :: hash
447
448 INTEGER(KIND=int_8), PARAMETER :: b32 = 2_int_8**32 - 1_int_8
449
450 INTEGER :: i
451
452 hash = 0_int_8
453 DO i = 1, len(key)
454 hash = iand(hash + ichar(key(i:i)), b32)
455 hash = iand(hash + iand(ishft(hash, 10), b32), b32)
456 hash = iand(ieor(hash, iand(ishft(hash, -6), b32)), b32)
457 END DO
458 hash = iand(hash + iand(ishft(hash, 3), b32), b32)
459 hash = iand(ieor(hash, iand(ishft(hash, -11), b32)), b32)
460 hash = iand(hash + iand(ishft(hash, 15), b32), b32)
461 END FUNCTION routine_map_hash_function
462
463! **************************************************************************************************
464!> \brief ...
465!> \param key ...
466!> \return ...
467! **************************************************************************************************
468 PURE FUNCTION routine_map_keys_equal(key1, key2) RESULT(res)
469 CHARACTER(LEN=*), INTENT(IN) :: key1, key2
470 LOGICAL :: res
471
472 res = (key1 == key2)
473 END FUNCTION routine_map_keys_equal
474
475END 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.