Line data Source code
1 : !!****m* ABINIT/m_hastable
2 : !! NAME
3 : !! m_hashtable
4 : !!
5 : !! FUNCTION
6 : !! This module provide a string: value pair hash table
7 : !! COPYRIGHT
8 : !! Taken from http://fortranwiki.org/fortran/show/hash+table+example
9 : !! The code is originally written by Izaak Beekman under the LGPL license.
10 : !! Adapted for usage in Abinit by hexu
11 : !!
12 : !! Note: the behavior is different from the origial version
13 : !! The value will be overwritten in this version, whereas it is ignored in the
14 : !! original version if the key is already in the table (why??!!).
15 : !!
16 : !! Note2:!!!!!!!!!!!!!!!!! FIXME
17 : !! It does not handle white space at the end of string correctly. It does not affect
18 : !! the usage in Multibinit but BE CAREFUL.
19 : !!
20 : !! Below is the original Copyright.
21 : !!=======================================
22 : !! Copyright (C) Izaak Beekman 2010
23 : !! This program is free software: you can redistribute it and/or modify
24 : !! it under the terms of the GNU Lesser General Public License as published by
25 : !! the Free Software Foundation, either version 3 of the License, or
26 : !! (at your option) any later version.
27 : !!
28 : !! This program is distributed in the hope that it will be useful,
29 : !! but WITHOUT ANY WARRANTY; without even the implied warranty of
30 : !! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 : !! GNU Lesser General Public License for more details.
32 : !! You should have received a copy of the GNU Lesser General Public License
33 : !! along with this program. If not, see <http://www.gnu.org/licenses/>.
34 : !!
35 : !! SOURCE
36 :
37 : #if defined HAVE_CONFIG_H
38 : #include "config.h"
39 : #endif
40 :
41 : #include "abi_common.h"
42 :
43 : MODULE m_hashtable_strval
44 :
45 : !!***
46 : use defs_basis
47 : use m_errors
48 : use m_abicore
49 : use, intrinsic :: iso_c_binding
50 : !use, intrinsic :: iso_c_binding, only: c_double, c_int64_t
51 : !USE, INTRINSIC :: IEEE_ARITHMETIC
52 :
53 : !use, intrinsic :: ieee_arithmetic, only: IEEE_Value, IEEE_QUIET_NAN
54 : !use, intrinsic :: iso_fortran_env, only: real64
55 :
56 : IMPLICIT NONE ! Use strong typing
57 : INTEGER, PARAMETER :: tbl_size = 50
58 : !real(real64) :: nan
59 : !nan = IEEE_VALUE(nan, IEEE_QUIET_NAN)
60 : ! The above one is more standard, but how to make nan a parameter?
61 : ! The following is used instead.
62 : !real(c_double), parameter :: NAN=IEEE_VALUE(nan, IEEE_QUIET_NAN)
63 :
64 : !real(c_double), parameter :: NAN = TRANSFER(9218868437227405313_c_int64_t, 1._c_double)
65 : ! NOTE: this is not NAN really. The correct one is the last line. But NAG compiler does not think it is a valid floating number.
66 : ! real(c_double), parameter :: NAN = TRANSFER(921886843722740531_c_int64_t, 1._c_double)
67 :
68 : ! NB: We have to use SIZEOF_INT CPP macro giving the size of `int', as computed by sizeof.
69 : ! to declare the length of char buffers as in:
70 : !
71 : ! character(len=SIZEOF_INT * n) :: tmp
72 : !
73 : ! as cray-intel ftn and NAG do not support declaratations like:
74 : !
75 : !character(len=c_sizeof(key)) :: tmp
76 :
77 :
78 : TYPE sllist
79 : TYPE(sllist), POINTER :: child => NULL()
80 : CHARACTER(len=:), ALLOCATABLE :: key
81 : real(dp) :: val
82 : CONTAINS
83 : PROCEDURE :: put => put_sll
84 : PROCEDURE :: get => get_sll
85 : PROCEDURE :: free => free_sll
86 : PROCEDURE :: sum_val => sum_val_sll
87 : procedure :: sum_entry => sum_entry_sll
88 : procedure :: print_all => print_all_sll
89 : procedure :: print_entry => print_entry_sll
90 : END TYPE sllist
91 :
92 : TYPE hash_table_t
93 : TYPE(sllist), DIMENSION(:), ALLOCATABLE :: vec
94 : INTEGER :: vec_len = 0
95 : LOGICAL :: is_init = .FALSE.
96 : CONTAINS
97 : PROCEDURE :: init => init_hash_table_t
98 : PROCEDURE :: put => put_hash_table_t
99 : PROCEDURE :: get => get_hash_table_t
100 : PROCEDURE :: free => free_hash_table_t
101 : PROCEDURE :: sum_val => sum_val_hash_table_t
102 : PROCEDURE :: print_all => print_all_hash_table_t
103 : procedure :: print_entry => print_entry_hash_table_t
104 : procedure :: has_key
105 : procedure :: put_intn
106 : procedure :: get_intn
107 : procedure :: has_key_intn
108 : procedure :: put_int3
109 : procedure :: get_int3
110 : procedure :: has_key_int3
111 :
112 : END TYPE hash_table_t
113 :
114 : PUBLIC :: hash_table_t
115 :
116 :
117 : CONTAINS
118 :
119 150737 : RECURSIVE SUBROUTINE put_sll(list,key,val)
120 : CLASS(sllist), INTENT(inout) :: list
121 : CHARACTER(len=*), INTENT(in) :: key
122 : real(dp), intent(in) :: val
123 : INTEGER :: keylen
124 :
125 150737 : keylen = LEN(key)
126 150737 : IF (ALLOCATED(list%key)) THEN
127 140578 : IF (list%key /= key) THEN
128 45539 : IF ( .NOT. ASSOCIATED(list%child)) then
129 7639 : ABI_MALLOC_SCALAR(list%child)
130 : end IF
131 :
132 45539 : CALL put_sll(list%child,key,val)
133 : else
134 95039 : list%val=val
135 : END IF
136 : ELSE
137 : IF (.NOT. ALLOCATED(list%key)) &
138 10159 : ABI_MALLOC_TYPE_SCALAR(CHARACTER(len=keylen), list%key)
139 10159 : list%key = key
140 10159 : list%val = val
141 : END IF
142 150737 : END SUBROUTINE put_sll
143 :
144 :
145 2853775457 : RECURSIVE SUBROUTINE get_sll(list,key,val)
146 : CLASS(sllist), INTENT(in) :: list
147 : CHARACTER(len=*), INTENT(in) :: key
148 : real(dp), INTENT(out) :: val
149 : INTEGER :: vallen
150 :
151 2853775457 : vallen = 0
152 2853775457 : val=MAGIC_UNDEF
153 2853775457 : IF (ALLOCATED(list%key) .AND. (list%key == key)) THEN
154 29630178 : val = list%val
155 2824145279 : ELSE IF(ASSOCIATED(list%child)) THEN ! keep going
156 2215477367 : CALL get_sll(list%child,key,val)
157 : ELSE ! At the end of the list, no key found
158 : return
159 : END IF
160 : END SUBROUTINE get_sll
161 :
162 :
163 129319 : RECURSIVE SUBROUTINE free_sll(list)
164 : CLASS(sllist), INTENT(inout) :: list
165 129319 : IF (ASSOCIATED(list%child)) THEN
166 7639 : CALL free_sll(list%child)
167 7639 : ABI_FREE_SCALAR(list%child)
168 : END IF
169 129319 : list%child => NULL()
170 129319 : ABI_SFREE(list%key)
171 129319 : END SUBROUTINE free_sll
172 :
173 10504 : recursive function sum_val_sll(self) result(s)
174 : class(sllist), intent(in) :: self
175 : real(dp) :: s
176 10504 : s=0.0_dp
177 10504 : if (allocated(self%key)) then
178 909 : s=s+self%val
179 909 : if(associated(self%child)) then
180 404 : s=s+self%child%sum_val()
181 : endif
182 : end if
183 10504 : end function sum_val_sll
184 :
185 41600 : recursive function sum_entry_sll(self, label, prefix) result(s)
186 : class(sllist), intent(in) :: self
187 : character(*), optional, intent(in) :: label
188 : character(*), optional, intent(in) :: prefix
189 : real(dp) :: s
190 :
191 : integer :: length
192 :
193 41600 : if((.not. present(prefix)) .and. (.not. present(label))) then
194 0 : ABI_ERROR('Label and prefix not present.')
195 : endif
196 41600 : if(present(label) .and. present(prefix)) then
197 0 : ABI_ERROR('Label and prefix present, only specify one of the two.')
198 : endif
199 :
200 41600 : if(present(prefix)) length=len(prefix)
201 :
202 41600 : s=0.0_dp
203 41600 : if (allocated(self%key)) then
204 3600 : if(present(prefix)) then
205 900 : if(self%key(1:length) .eq. prefix) then
206 300 : s=s+self%val
207 : endif
208 : else
209 2700 : if(self%key .eq. label) then
210 600 : s=s+self%val
211 : endif
212 : endif
213 3600 : if(associated(self%child)) then
214 3200 : s=s+self%child%sum_entry(label=label, prefix=prefix)
215 : endif
216 : end if
217 41600 : end function sum_entry_sll
218 :
219 0 : recursive subroutine print_all_sll(self)
220 : class(sllist), intent(in) :: self
221 : character(len=80) :: msg
222 :
223 0 : if (allocated(self%key)) then
224 0 : write(msg, "(A40, 1X, ES13.5)") self%key, self%val
225 0 : call wrtout(std_out,msg,'COLL')
226 0 : call wrtout(ab_out, msg, 'COLL')
227 0 : if(associated(self%child)) then
228 0 : call self%child%print_all()
229 : endif
230 : end if
231 0 : end subroutine print_all_sll
232 :
233 416 : recursive subroutine print_entry_sll(self, label, prefix)
234 : class(sllist), intent(in) :: self
235 : character(*), optional, intent(in) :: label
236 : character(*), optional, intent(in) :: prefix
237 :
238 : integer :: length
239 : character(len=80) :: msg
240 :
241 416 : if((.not. present(prefix)) .and. (.not. present(label))) then
242 0 : ABI_ERROR('Label and prefix not present.')
243 : endif
244 416 : if(present(label) .and. present(prefix)) then
245 0 : ABI_ERROR('Label and prefix present, only specify one of the two.')
246 : endif
247 :
248 416 : if(present(prefix)) then
249 104 : length=len(prefix)
250 : endif
251 :
252 416 : if (allocated(self%key)) then
253 36 : if(present(prefix)) then
254 9 : if(self%key(1:length) .eq. prefix) then
255 3 : write(msg, "(A40, 1X, ES13.5)") self%key, self%val
256 3 : call wrtout(std_out,msg,'COLL')
257 3 : call wrtout(ab_out, msg, 'COLL')
258 : endif
259 : else !not prefix but label present
260 27 : if(self%key .eq. label) then
261 6 : write(msg, "(A40, 1X, ES13.5)") self%key, self%val
262 6 : call wrtout(std_out,msg,'COLL')
263 6 : call wrtout(ab_out, msg, 'COLL')
264 : endif
265 : endif
266 36 : if(associated(self%child)) then
267 32 : call self%child%print_entry(label=label, prefix=prefix)
268 : endif
269 : endif
270 :
271 416 : end subroutine print_entry_sll
272 :
273 66 : SUBROUTINE init_hash_table_t(tbl,tbl_len)
274 : CLASS(hash_table_t), INTENT(inout) :: tbl
275 : INTEGER, OPTIONAL, INTENT(in) :: tbl_len
276 :
277 66 : ABI_SFREE(tbl%vec)
278 66 : IF (PRESENT(tbl_len)) THEN
279 121560 : ABI_MALLOC(tbl%vec, (tbl_len))
280 60 : tbl%vec_len = tbl_len
281 : ELSE
282 306 : ABI_MALLOC(tbl%vec, (tbl_size))
283 6 : tbl%vec_len = tbl_size
284 : END IF
285 66 : tbl%is_init = .TRUE.
286 66 : END SUBROUTINE init_hash_table_t
287 :
288 : ! The first part of the hashing procedure using the string
289 : ! collating sequence
290 638403288 : ELEMENTAL FUNCTION sum_string(str) RESULT(sig)
291 : CHARACTER(len=*), INTENT(in) :: str
292 : INTEGER :: sig
293 638403288 : CHARACTER, DIMENSION(LEN(str)) :: tmp
294 : INTEGER :: i
295 :
296 10347013160 : FORALL (i=1:LEN(str))
297 : tmp(i) = str(i:i)
298 : END FORALL
299 10347013160 : sig = SUM(ICHAR(tmp))
300 638403288 : END FUNCTION sum_string
301 :
302 :
303 105198 : SUBROUTINE put_hash_table_t(tbl,key,val)
304 : CLASS(hash_table_t), INTENT(inout) :: tbl
305 : CHARACTER(len=*), INTENT(in) :: key
306 : real(dp), INTENT(in) :: val
307 : INTEGER :: hash
308 :
309 105198 : hash = MOD(sum_string(key),tbl%vec_len) +1
310 105198 : CALL tbl%vec(hash)%put(key=key,val=val)
311 105198 : END SUBROUTINE put_hash_table_t
312 :
313 :
314 638298090 : function get_hash_table_t(tbl,key) result(val)
315 : CLASS(hash_table_t), INTENT(in) :: tbl
316 : CHARACTER(len=*), INTENT(in) :: key
317 : real(dp) :: val
318 : INTEGER :: hash
319 :
320 638298090 : hash = MOD(sum_string(key),tbl%vec_len) + 1
321 638298090 : CALL tbl%vec(hash)%get(key=key,val=val)
322 638298090 : END function get_hash_table_t
323 :
324 :
325 66 : SUBROUTINE free_hash_table_t(tbl)
326 : CLASS(hash_table_t), INTENT(inout) :: tbl
327 : INTEGER :: i, low, high
328 :
329 66 : low = LBOUND(tbl%vec,dim=1)
330 66 : high = UBOUND(tbl%vec,dim=1)
331 66 : IF (ALLOCATED(tbl%vec)) THEN
332 121746 : DO i=low,high
333 121746 : CALL tbl%vec(i)%free()
334 : END DO
335 121746 : ABI_FREE(tbl%vec)
336 : END IF
337 66 : tbl%is_init = .FALSE.
338 66 : END SUBROUTINE free_hash_table_t
339 :
340 638298090 : function has_key(self, key)
341 : class(hash_table_t), intent(in) :: self
342 : character(*), intent(in) :: key
343 : logical :: has_key
344 638298090 : has_key=(self%get(key)/=MAGIC_UNDEF)
345 638298090 : end function has_key
346 :
347 1002 : function sum_val_hash_table_t(self, label, prefix) result(s)
348 : class(hash_table_t), intent(in) :: self
349 : character(len=*), optional, intent(in) :: label, prefix
350 :
351 : real(dp) :: s
352 : integer :: i
353 1002 : s=0.0_dp
354 1002 : if (.not.(self%is_init)) then
355 : return
356 : end if
357 51102 : do i =1, self%vec_len
358 51102 : if(present(label) .or. present(prefix)) then
359 80000 : s=s+self%vec(i)%sum_entry(label=label, prefix=prefix)
360 : else
361 10100 : s=s+ self%vec(i)%sum_val()
362 : endif
363 : end do
364 : end function sum_val_hash_table_t
365 :
366 0 : subroutine print_all_hash_table_t(self)
367 : class(hash_table_t), intent(in) :: self
368 : integer :: i, low, high
369 0 : low = LBOUND(self%vec,dim=1)
370 0 : high = UBOUND(self%vec,dim=1)
371 :
372 0 : if (allocated(self%vec)) then
373 0 : do i =low, high
374 0 : call self%vec(i)%print_all()
375 : end do
376 : end if
377 0 : end subroutine print_all_hash_table_t
378 :
379 8 : subroutine print_entry_hash_table_t(self, label, prefix)
380 : class(hash_table_t), intent(in) :: self
381 : character(len=*), optional, intent(in) :: label, prefix
382 : integer :: i, low, high
383 8 : low = LBOUND(self%vec,dim=1)
384 8 : high = UBOUND(self%vec,dim=1)
385 :
386 8 : if (allocated(self%vec)) then
387 408 : do i =low, high
388 808 : call self%vec(i)%print_entry(label=label, prefix=prefix)
389 : end do
390 : end if
391 8 : end subroutine print_entry_hash_table_t
392 :
393 10142 : subroutine put_intn(self, key, val, n)
394 : class(hash_table_t), intent(inout) :: self
395 : integer :: n
396 : integer, intent(in) :: key(n)
397 : real(dp) :: val
398 : !character(len=c_sizeof(key)) :: tmp
399 10142 : character(len=SIZEOF_INT * n) :: tmp
400 10142 : call self%put(transfer(key, tmp), val)
401 10142 : end subroutine put_intn
402 :
403 0 : function get_intn(self, key,n) result(val)
404 : class(hash_table_t), intent(inout) :: self
405 : integer, intent(in) :: n
406 : integer, intent(in) :: key(n)
407 : real(dp) :: val
408 : !character(len=c_sizeof(key)) :: tmp
409 0 : character(len=SIZEOF_INT * n) :: tmp
410 0 : val = self%get(transfer(key, tmp))
411 0 : end function get_intn
412 :
413 638298090 : function has_key_intn(self, key, n) result(val)
414 : class(hash_table_t), intent(inout) :: self
415 : integer, intent(in) :: n
416 : integer, intent(in) :: key(n)
417 : logical :: val
418 : !character(len=c_sizeof(key)) :: tmp
419 638298090 : character(len=SIZEOF_INT * n) :: tmp
420 638298090 : val = self%has_key(transfer(key, tmp))
421 638298090 : end function has_key_intn
422 :
423 :
424 :
425 0 : subroutine put_int3(self, key, val)
426 : class(hash_table_t), intent(inout) :: self
427 : integer, intent(in) :: key(3)
428 : real(dp) :: val
429 : character(len=SIZEOF_INT * 3) :: tmp
430 : !character(len=c_sizeof(key)) :: tmp
431 0 : call self%put(transfer(key, tmp), val)
432 0 : end subroutine put_int3
433 :
434 :
435 0 : function get_int3(self, key) result(val)
436 : class(hash_table_t), intent(inout) :: self
437 : integer, intent(in) :: key(3)
438 : real(dp) :: val
439 : character(len=12) :: tmp
440 0 : val = self%get(transfer(key, tmp))
441 0 : end function get_int3
442 :
443 :
444 0 : function has_key_int3(self, key) result(val)
445 : class(hash_table_t), intent(inout) :: self
446 : integer, intent(in) :: key(3)
447 : logical :: val
448 : character(len=12) :: tmp
449 0 : val = self%has_key(transfer(key, tmp))
450 0 : end function has_key_int3
451 :
452 :
453 :
454 0 : end module m_hashtable_strval
|