The TELEMAC-MASCARET system  trunk
mod_hash_table.f
Go to the documentation of this file.
1 ! *********************
2  MODULE mod_hash_table
3 ! *********************
4 !
5 !***********************************************************************
6 ! PARTEL
7 !***********************************************************************
8 !
9 !BRIEF Functions to handle hash_table for partel
10 !
12  IMPLICIT NONE
13 !
14  !THIS HASH TABLE IS SPECIFICALY MADE FOR PARTEL AND IS ABSOLUTELY
15  !NOT GENERIC.
16  !THIS HASH TABLE PROVIDE INSERTION AND SEARCH BUT NO DELETION
17  !
18  !THE IMPLEMENTATION USE OPEN ADDRESSING WITH LINEAR PROBING FOR
19  !COLLISION RESOLUTION
20 !
21  INTEGER, PARAMETER :: unused = -1
22 !
23  !THIS TYPE IS THE ELEMENT CONTAINED BY THE HASH TABLE.
24  !THE X AND Y REPRESENTS THE INDICES OF THE ARRAYS PARTEL USED
25  !PREVIOUSLY. SO FROM THE USER POINT OF VIEW, X AND Y ARE ALWAYS
26  !POSITIVE BUT INTERNALY THE HASH TABLE USE NEGATIVE VALUE TO
27  !REPRESENTS AN UNUSED ELEMENT, WHICH MEANS THAHT THE HASH TABLE
28  !IS FREE TO INSERT AT THIS ELEMENT
29  !
30  !V CAN BE POSITIVE OR NEGATIVE
31  TYPE hash_table_el
32  INTEGER :: x=unused
33  INTEGER :: y, v
34  END TYPE
35 !
36  TYPE hash_table
37  TYPE(hash_table_el), ALLOCATABLE :: table(:)
38 !
39  !THE NUMBER OF INSERTED ELEMENTS IN THE HASH TABLE
40  INTEGER :: nelts = 0
41 !
42  !THE DEFAULT VALUE RETURNED WHEN THE USER SEARCH FOR A
43  !VALUE WHICH IS NOT CONTAINED BY THE HASH TABLE
44  INTEGER :: default_value = 0
45 !
46  !THE MAXIMUM NUMBER OF CONSECUTIVE ELEMENTS WE HAVE TO
47  !BROWSE WHEN SEARCHING FOR A VALUE
48  INTEGER :: longuest_probe = 0
49 !
50  !WHEN THE RATIO 'NO. OF ELTS / SIZE OF THE HASH TABLE' IS
51  !SUPERIOR TO MAX_LOAD_FACTOR, THE TABLE WILL GROW
52  REAL :: max_load_factor = 0.75
53  END TYPE hash_table
54 !
55  CONTAINS
56 !***********************************************************************
57  SUBROUTINE hash_table_create
58 !***********************************************************************
59 !
60  &(ht, table_size)
61 !
62 !***********************************************************************
63 ! PARTEL 27/02/2018
64 !***********************************************************************
65 !
66 !brief The newly created hash table will have at least 1M elements.
67 ! If the user ask for a size which is not a power of two, the
68 ! size will be rounded to the nearest superior power of two
69 !
70 !history Judicaël Grasset (Daresbury Lab & EDF)
71 !+ 27/02/2018
72 !
73 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 !| HT |<->| Hash table
75 !| TABLE_SIZE |-->| The required size
76 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
77 !
78  TYPE(hash_table), INTENT(INOUT) :: HT
79  INTEGER, INTENT(IN) :: TABLE_SIZE
80  INTEGER :: IERR, P2
81 !
82  p2 = nearest_superior_power_2(table_size)
83  ALLOCATE(ht%TABLE(p2), stat=ierr)
84  CALL check_allocate(ierr, 'HASH TABLE CREATION')
85  END SUBROUTINE
86 !
87 !***********************************************************************
88  SUBROUTINE hash_table_destroy
89 !***********************************************************************
90 !
91  &(ht)
92 !
93 !***********************************************************************
94 ! PARTEL 27/02/2018
95 !***********************************************************************
96 !
97 !brief Destroy a hash table and free his memory.
98 !
99 !history Judicaël Grasset (Daresbury Lab & EDF)
100 !+ 27/02/2018
101 !
102 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
103 !| HT |<->| Hash table to destroy
104 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105 !
106  TYPE(hash_table), INTENT(INOUT) :: HT
107 !
108  ht%NELTS=-1
109  ht%DEFAULT_VALUE=-1
110  ht%LONGUEST_PROBE=-1
111  IF(ALLOCATED(ht%TABLE)) DEALLOCATE(ht%TABLE)
112  END SUBROUTINE
113 !***********************************************************************
114  PURE FUNCTION nearest_superior_power_2
115 !***********************************************************************
116 !
117  &(x) result(p2)
118 !
119 !***********************************************************************
120 ! PARTEL 27/02/2018
121 !***********************************************************************
122 !
123 !brief Compute the nearest power of two of X which is at least 2**20
124 !
125 !history Judicaël Grasset (Daresbury Lab & EDF)
126 !+ 27/02/2018
127 !
128 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
129 !| X |-->| Minimum required by user
130 !| P2 |<--| Nearest power of two found (at least 2**20)
131 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
132 !
133  INTEGER, INTENT(IN) :: X
134  INTEGER :: P2
135  !WILL SET THE DEFAULT SIZE OF THE HASHTABLE TO
136  !1MIB OF ELEMENTS
137  p2 = 2**20
138 !
139  DO WHILE(p2 < x)
140  p2 = p2*2
141  END DO
142  END FUNCTION
143 !***********************************************************************
144  PURE FUNCTION elegant_pairing
145 !***********************************************************************
146 !
147  &(x,y) result(pairing)
148 !
149 !***********************************************************************
150 ! PARTEL 27/02/2018
151 !***********************************************************************
152 !
153 !brief Elegant pairing,algorithm by matthew szudzik(wolfram research)
154 !
155 !history Judicaël Grasset (Daresbury Lab & EDF)
156 !+ 27/02/2018
157 !
158 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
159 !| X |-->| First number to paired
160 !| Y |-->| Second Number to paired
161 !| PAIRING |<--| Pairing of X and Y
162 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
163 !
164  INTEGER, INTENT(IN) :: X,Y
165  INTEGER(KIND=K8) :: PAIRING
166 
167  IF (x >= y) THEN
168  pairing = int(x, kind=k8) * x + x + y
169  ELSE
170  pairing = y * y + x
171  END IF
172  END FUNCTION elegant_pairing
173 !
174 !***********************************************************************
175  PURE FUNCTION hash
176 !***********************************************************************
177 !
178  &(k, table_size) result(h)
179 !
180 !***********************************************************************
181 ! PARTEL 27/02/2018
182 !***********************************************************************
183 !
184 !brief Hide the real hash function from the user
185 !
186 !history Judicaël Grasset (Daresbury Lab & EDF)
187 !+ 27/02/2018
188 !
189 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
190 !| K |-->| Key to hash
191 !| TABLE_SIZE |-->| Size of the hash table
192 !| H |<--| Computed hash
193 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
194 !
195  INTEGER(KIND=K8), INTENT(IN) :: K
196  INTEGER, INTENT(IN) :: TABLE_SIZE
197  INTEGER :: H
198 !
199  !ADD ONE TO BE IN THE RANGE OF FORTRAN ARRAY (1:N)
200  h=fingerprint(k,int(table_size, k8))+1
201  END FUNCTION hash
202 !
203 !***********************************************************************
204  PURE FUNCTION fingerprint
205 !***********************************************************************
206 !
207  &(k, s) result(h)
208 !
209 !***********************************************************************
210 ! PARTEL 27/02/2018
211 !***********************************************************************
212 !
213 !brief Fingerprint is a sightly modified version of the fingerprint
214 ! from the farmhash(v1.1) framework of google (mit licence)
215 ! https://github.com/google/farmhash/blob/master/src/farmhash.h
216 !
217 !history Judicaël Grasset (Daresbury Lab & EDF)
218 !+ 27/02/2018
219 !
220 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
221 !| K |-->| Key to hash
222 !| S |-->| Size of the hash table
223 !| H |<--| Computed hash
224 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225 !
226  INTEGER(KIND=K8), INTENT(IN) :: K
227  INTEGER(KIND=K8), INTENT(IN) :: S
228  !MAGIC NUMBER FROM MURMURHASH 3
229  INTEGER(KIND=K8)::KMUL
230  INTEGER(KIND=K8) :: B
231  INTEGER :: H
232 #ifdef NAGFOR
233  !THIS CONSTANT COMES FROM KNUTH,
234  !THE ART OF COMPUTER PROGRAMMING,
235  !VOL.3, SORTING AND SEARCHING, 1973
236  DOUBLE PRECISION, PARAMETER :: A=(2.2360679774d0-1d0)/2d0
237 !
238  kmul=z'CC9E2D51'
239  h=floor(dble(s) * dmod(dble(k)*a,1d0))
240  h=abs(h)
241 #else
242 !
243  kmul=z'CC9E2D51'
244  b = k*kmul
245  b = ieor(b, ishft(b,44))
246  b = b*kmul
247  b = ieor(b, ishft(b,41))
248  b = b*kmul
249 !
250  !IN THE ORIGINAL FINGERPRINT FROM HASHFARM, ONLY UNSIGNED
251  !INTEGER WERE USED, SO WHEN THE INT OVERFLOW IT WAS
252  !POSITIVE, BUT WE DO NOT HAVE UNSIGNED IN FORTRAN
253  b = abs(b)
254 !
255  !WE CAN DO THE MODULO BY BITSHIFT BECAUSE WE KNOW THE SIZE
256  !IS A POWER OF TWO. DOING THIS WAY SHOULD BE REALLY FASTER
257  !THAN USING THE MODULO
258  !B = MOD(B,SK16)
259  b = iand(b, s-1)
260 !
261  !B CAN BE CASTED TO A SMALLER INTEGER SINCE IT HAVE
262  !BEEN REDUCED BY A MODULO OF THE HASHTABLE SIZE, WHICH IS
263  !ALWAYS CONTAINED INTO AN INT
264  h = int(b)
265 #endif
266  END FUNCTION
267 !***********************************************************************
268  RECURSIVE SUBROUTINE hash_table_insert
269 !***********************************************************************
270 !
271  &(ht, x, y, v)
272 !
273 !***********************************************************************
274 ! PARTEL 27/02/2018
275 !***********************************************************************
276 !
277 !brief Insert an element into the hash table. Will make the hash to
278 ! table grow if necessary (See max_load_factor).
279 !
280 !history Judicaël Grasset (Daresbury Lab & EDF)
281 !+ 27/02/2018
282 !
283 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
284 !| HT |<->| Hash table to insert into
285 !| X |-->| X coordinate of the element
286 !| Y |-->| Y coordinate of the element
287 !| V |-->| Value to insert
288 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
289 !
290  TYPE(hash_table), INTENT(INOUT) :: HT
291  INTEGER, INTENT(IN) :: X,Y,V
292  INTEGER :: I, PROBE_SIZE
293  INTEGER(KIND=K8) :: K
294 
295  IF(x < 0 .OR. y < 0) THEN
296  WRITE(lu,*)'X AND Y MUST BE POSITIVE'
297  CALL plante(1,'MOD_HASH_TABLE')
298  END IF
299 !
300  !CHECK IF IT'S TIME TO GROW
301  IF(REAL(ht%nelts)/SIZE(ht%table)>HT%MAX_LOAD_FACTOR) then
302  CALL hash_table_grow(ht)
303  END IF
304 !
305  !TRANSFORM THE COORDINATES INTO A SINGLE INTEGER
306  k = elegant_pairing(x,y)
307 !
308  i = hash(k, SIZE(ht%TABLE))
309 !
310  probe_size = 0
311 !
312  DO WHILE (ht%TABLE(i)%X /= unused)
313 !
314  !TEST IF IT'S A MODIFICATION OF AN ALREADY EXISTING ELT
315  IF(ht%TABLE(i)%X == x .AND. ht%TABLE(i)%Y == y)EXIT
316 !
317  i = i+1
318  probe_size = probe_size+1
319 !
320  !IF WE ARE AT THE END OF THE TABLE, CONTINUE TO SEARCH
321  !FROM THE BEGINING
322  IF(i>SIZE(ht%TABLE)) i=1
323  END DO
324 !
325  ht%TABLE(i)%X = x
326  ht%TABLE(i)%Y = y
327  ht%TABLE(i)%V = v
328 !
329  IF(ht%LONGUEST_PROBE < probe_size) THEN
330  ht%LONGUEST_PROBE=probe_size
331  END IF
332 !
333  ht%NELTS = ht%NELTS+1
334  END SUBROUTINE
335 !***********************************************************************
336  FUNCTION hash_table_get
337 !***********************************************************************
338 !
339  &(ht, x, y) result(v)
340 !
341 !***********************************************************************
342 ! PARTEL 27/02/2018
343 !***********************************************************************
344 !
345 !brief Return the value contained in the couple (x,y), will return
346 ! default_value if the couple is not found
347 !
348 !history Judicaël Grasset (Daresbury Lab & EDF)
349 !+ 27/02/2018
350 !
351 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
352 !| HT |-->| Hash table to insert into
353 !| X |-->| X coordinate of the element
354 !| Y |-->| Y coordinate of the element
355 !| V |<--| Retrieved value
356 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
357 !
358  TYPE(hash_table), INTENT(IN) :: HT
359  INTEGER, INTENT(IN) :: X, Y
360  INTEGER :: I, V
361  INTEGER(KIND=K8) :: K
362 
363  IF(x < 0 .OR. y < 0) THEN
364  WRITE(lu,*)'X AND Y MUST BE POSITIVE'
365  CALL plante(1,'MOD_HASH_TABLE')
366  END IF
367 !
368  v = ht%DEFAULT_VALUE
369 !
370  k = elegant_pairing(x,y)
371  i = hash(k, SIZE(ht%TABLE))
372 !
373  DO WHILE(ht%TABLE(i)%X /= unused)
374  IF(ht%TABLE(i)%X == x .AND. ht%TABLE(i)%Y == y) THEN
375  v = ht%TABLE(i)%V
376  EXIT
377  END IF
378  i = i+1
379  IF(i>SIZE(ht%TABLE)) i = 1
380  END DO
381  END FUNCTION hash_table_get
382 !***********************************************************************
383  SUBROUTINE hash_table_grow
384 !***********************************************************************
385 !
386  &(ht)
387 !
388 !***********************************************************************
389 ! PARTEL 27/02/2018
390 !***********************************************************************
391 !
392 !brief Increase the size of the hash table, currently by doubling it
393 !
394 !history Judicaël Grasset (Daresbury Lab & EDF)
395 !+ 27/02/2018
396 !
397 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
398 !| HT |-->| Hash table to grow
399 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
400 !
401  TYPE(hash_table), INTENT(INOUT) :: HT
402  TYPE(hash_table) :: NEW_HT
403  INTEGER :: I
404 !
405  CALL hash_table_create(new_ht, SIZE(ht%TABLE)*2)
406 !
407  DO i=1,SIZE(ht%TABLE)
408  IF(ht%TABLE(i)%X /= unused) THEN
409  CALL hash_table_insert(new_ht,
410  & ht%TABLE(i)%X, ht%TABLE(i)%Y, ht%TABLE(i)%V)
411  END IF
412  END DO
413 !
414 #ifdef NAGFOR
415  ALLOCATE(ht%TABLE(SIZE(new_ht%TABLE)))
416  ht%TABLE = new_ht%TABLE
417  DEALLOCATE(new_ht%TABLE)
418 #else
419  CALL move_alloc(new_ht%TABLE, ht%TABLE)
420 #endif
421  ht%LONGUEST_PROBE = new_ht%LONGUEST_PROBE
422  CALL hash_table_destroy(new_ht)
423  END SUBROUTINE hash_table_grow
424 !
425 !***********************************************************************
426  SUBROUTINE hash_table_stat
427 !***********************************************************************
428 !
429  &(ht)
430 !
431 !***********************************************************************
432 ! PARTEL 27/02/2018
433 !***********************************************************************
434 !
435 !brief Print some statistics about an hash table
436 !
437 !history Judicaël Grasset (Daresbury Lab & EDF)
438 !+ 27/02/2018
439 !
440 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
441 !| HT |-->| Hash table
442 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
443 !
444  TYPE(hash_table), INTENT(IN) :: HT
445  WRITE(lu,*)'LARGEST NO. OF COLLISIONS',ht%LONGUEST_PROBE
446  WRITE(lu,*)'NUMBER OF BUCKETS:',SIZE(ht%TABLE)
447  WRITE(lu,*)'NUMBER OF ELEMENTS:',ht%NELTS
448  END SUBROUTINE
449 !
450 !***********************************************************************
451  SUBROUTINE hash_table_test
452 !***********************************************************************
453 !
454  &()
455 !
456 !***********************************************************************
457 ! PARTEL 27/02/2018
458 !***********************************************************************
459 !
460 !brief Some tests to check that the hash table implementations is
461 ! working. Only usefull for debugging.
462 !
463 !history Judicaël Grasset (Daresbury Lab & EDF)
464 !+ 27/02/2018
465 !
466 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
467 !| HT |-->| Hash table
468 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
469 !
470  TYPE(hash_table) :: HT
471  INTEGER :: V,I
472 !
473  CALL hash_table_create(ht, 1024)
474 !
475  !SIMPLE INSERT
476  CALL hash_table_insert(ht, 10,10,10)
477  v=hash_table_get(ht,10,10)
478  IF (v/=10) THEN
479  WRITE(lu,*)'FOUND',v,'SHOULD HAVE BEEN 10'
480  CALL plante(1,'MOD_HASH_TABLE')
481  END IF
482 !
483  !SIMPLE INSERT WITH NEGATIVE VALUE
484  CALL hash_table_insert(ht, 10,20,-10)
485  v=hash_table_get(ht,10,20)
486  IF (v/=-10) THEN
487  WRITE(lu,*)'FOUND',v,'SHOULD HAVE BEEN -10'
488  CALL plante(1,'MOD_HASH_TABLE')
489  END IF
490 !
491  !INSERT WITH GIANTIC X Y
492  CALL hash_table_insert(ht, 1234567890,1234567890,42)
493  v=hash_table_get(ht,1234567890,1234567890)
494  IF (v/=42) THEN
495  WRITE(lu,*)'FOUND',v,'SHOULD HAVE BEEN 42'
496  CALL plante(1,'MOD_HASH_TABLE')
497  END IF
498 !
499  !INSERT LOT OF DATA
500  DO i=100,4298500
501  CALL hash_table_insert(ht,i,i,i)
502  END DO
503  DO i=100,4298500
504  v=hash_table_get(ht,i,i)
505  IF (v /= i) THEN
506  WRITE(lu,*)'FOUND',v,'SHOULD HAVE BEEN',i
507  CALL plante(1,'MOD_HASH_TABLE')
508  END IF
509  END DO
510 !
511  !CHANGE THE VALUE OF AN ALREADY EXISTING ELEMENT
512  CALL hash_table_insert(ht,10,10,2017)
513  v=hash_table_get(ht,10,10)
514  IF (v /= 2017) THEN
515  WRITE(lu,*)'FOUND',v,'SHOULD HAVE BEEN 2017'
516  CALL plante(1,'MOD_HASH_TABLE')
517  END IF
518 !
519  CALL hash_table_stat(ht)
520  CALL hash_table_destroy(ht)
521  WRITE(lu,*)'HASH_TABLE: ALL TEST PASSED'
522  END SUBROUTINE hash_table_test
523 !
524  END MODULE mod_hash_table
integer, parameter k8
subroutine hash_table_create(HT, TABLE_SIZE)
subroutine hash_table_stat(HT)
pure integer(kind=k8) function elegant_pairing(X, Y)
subroutine hash_table_test()
integer, parameter unused
pure integer function nearest_superior_power_2(X)
pure integer function fingerprint(K, S)
integer function hash_table_get(HT, X, Y)
subroutine hash_table_destroy(HT)
recursive subroutine hash_table_insert(HT, X, Y, V)
pure integer function hash(K, TABLE_SIZE)
subroutine hash_table_grow(HT)