FrontISTR  5.9.0
Large-scale structural analysis program with finit element method
dictionary.f90
Go to the documentation of this file.
1 !-------------------------------------------------------------------------------
2 ! Copyright (c) 2019 FrontISTR Commons
3 ! This software is released under the MIT License, see LICENSE.txt
4 !-------------------------------------------------------------------------------
5 ! dictionary.f90 --
6 ! Include file for defining dictionaries:
7 ! a mapping of strings to some data
8 !
9 ! See the example/test program for the way to use this
10 !
11 ! Note:
12 ! Use is made of a hash table. This should speed up most
13 ! operations. The algorithm for determining the hashkey
14 ! is taken from Kernighan and Pike: The Practice of Programming
15 !
16 ! Note:
17 ! - Define the length of the strings as
18 ! parameter "DICT_KEY_LENGTH"
19 ! - Define a derived type for the data
20 ! to be stored
21 ! - Also define a "null" value - DICT_NULL
22 ! of type DICT_DATA, for use when the
23 ! key is not found.
24 ! - Put both in a separate module, that
25 ! will be used.
26 !
27 ! $Id: dictionary.f90,v 1.3 2007/01/26 09:56:43 arjenmarkus Exp $
28 !
29 ! Following is modified by Xi YUAN( AdvanceSoft )
30 ! - In function dict_hashkey, there would be value of dict_hashkey
31 ! exceeds max integer value. It maybe avoided by extending the max
32 ! value of this integer or use small value of multiplier. Take care!
33 ! It hasn't be solved completely.
34 !
35 
36 type list_data
37  character(len=DICT_KEY_LENGTH) :: key
38  type(DICT_DATA) :: value
39 end type list_data
40 
41 type hash_list
42  type(LINKED_LIST), pointer :: list
43 end type hash_list
44 
45 type dict_struct
46  private
47  type(HASH_LIST), pointer, dimension(:) :: table
48 end type dict_struct
49 
50 !
51 ! We do not want everything to be public
52 !
53 private :: list_data
54 private :: hash_list
55 private :: linked_list
56 private :: list_create
57 private :: list_destroy
58 private :: list_count
59 private :: list_next
60 private :: list_insert
61 private :: list_delete_element
62 private :: dict_get_elem
63 private :: dict_hashkey
64 
65 integer, parameter, private :: hash_size = 499
66 integer, parameter, private :: multiplier = 1
67 
68 include 'linkedlist.f90'
69 
70 !
71 ! Routines and functions specific to dictionaries
72 !
73 
74 ! dict_create --
75 ! Create and initialise a dictionary
76 ! Arguments:
77 ! dict Pointer to new dictionary
78 ! key Key for the first element
79 ! value Value for the first element
80 ! Note:
81 ! This version assumes a shallow copy is enough
82 ! (that is, there are no pointers within the data
83 ! to be stored)
84 ! It also assumes the argument list does not already
85 ! refer to a list. Use dict_destroy first to
86 ! destroy up an old list.
87 !
88 subroutine dict_create( dict, key, value )
89  type(DICT_STRUCT), pointer :: dict
90  character(len=*), intent(in) :: key
91  type(DICT_DATA), intent(in) :: value
92 
93  type(LIST_DATA) :: data
94  integer :: i
95  integer :: hash
96 
97  allocate( dict )
98  allocate( dict%table(hash_size) )
99 
100  do i = 1,hash_size
101  dict%table(i)%list => null()
102  enddo
103 
104  data%key = key
105  data%value = value
106 
107  hash = dict_hashkey( trim(key ) )
108  call list_create( dict%table(hash)%list, data )
109 
110 end subroutine dict_create
111 
112 ! dict_destroy --
113 ! Destroy an entire dictionary
114 ! Arguments:
115 ! dict Pointer to the dictionary to be destroyed
116 ! Note:
117 ! This version assumes that there are no
118 ! pointers within the data that need deallocation
119 !
120 subroutine dict_destroy( dict )
121  type(DICT_STRUCT), pointer :: dict
122 
123  integer :: i
124 
125  do i = 1,size(dict%table)
126  if ( associated( dict%table(i)%list ) ) then
127  call list_destroy( dict%table(i)%list )
128  endif
129  enddo
130  deallocate( dict%table )
131  deallocate( dict )
132 
133 end subroutine dict_destroy
134 
135 ! dict_add_key
136 ! Add a new key
137 ! Arguments:
138 ! dict Pointer to the dictionary
139 ! key Key for the new element
140 ! value Value for the new element
141 ! Note:
142 ! If the key already exists, the
143 ! key's value is simply replaced
144 !
145 subroutine dict_add_key( dict, key, value )
146  type(DICT_STRUCT), pointer :: dict
147  character(len=*), intent(in) :: key
148  type(DICT_DATA), intent(in) :: value
149 
150  type(LIST_DATA) :: data
151  type(LINKED_LIST), pointer :: elem
152  integer :: hash
153 
154  elem => dict_get_elem( dict, key )
155 
156  if ( associated(elem) ) then
157  elem%data%value = value
158  else
159  data%key = key
160  data%value = value
161  hash = dict_hashkey( trim(key) )
162  if ( associated( dict%table(hash)%list ) ) then
163  call list_insert( dict%table(hash)%list, data )
164  else
165  call list_create( dict%table(hash)%list, data )
166  endif
167  endif
168 
169 end subroutine dict_add_key
170 
171 ! dict_delete_key
172 ! Delete a key-value pair from the dictionary
173 ! Arguments:
174 ! dict Dictionary in question
175 ! key Key to be removed
176 !
177 subroutine dict_delete_key( dict, key )
178  type(DICT_STRUCT), pointer :: dict
179  character(len=*), intent(in) :: key
180 
181  type(LINKED_LIST), pointer :: elem
182  integer :: hash
183 
184  elem => dict_get_elem( dict, key )
185 
186  if ( associated(elem) ) then
187  hash = dict_hashkey( trim(key) )
188  call list_delete_element( dict%table(hash)%list, elem )
189  endif
190 end subroutine dict_delete_key
191 
192 ! dict_get_key
193 ! Get the value belonging to a key
194 ! Arguments:
195 ! dict Pointer to the dictionary
196 ! key Key for which the values are sought
197 !
198 function dict_get_key( dict, key ) result(value)
199  type(DICT_STRUCT), pointer :: dict
200  character(len=*), intent(in) :: key
201  type(DICT_DATA), pointer :: value
202 
203  type(LINKED_LIST), pointer :: elem
204 
205  elem => dict_get_elem( dict, key )
206 
207  if ( associated(elem) ) then
208  value => elem%data%value
209  else
210  nullify(value)
211  endif
212 end function dict_get_key
213 
214 ! dict_has_key
215 ! Check if the dictionary has a particular key
216 ! Arguments:
217 ! dict Pointer to the dictionary
218 ! key Key to be sought
219 !
220 function dict_has_key( dict, key ) result(has)
221  type(DICT_STRUCT), pointer :: dict
222  character(len=*), intent(in) :: key
223  logical :: has
224 
225  type(LINKED_LIST), pointer :: elem
226 
227  elem => dict_get_elem( dict, key )
228 
229  has = associated(elem)
230 end function dict_has_key
231 
232 ! dict_get_elem
233 ! Find the element with a particular key
234 ! Arguments:
235 ! dict Pointer to the dictionary
236 ! key Key to be sought
237 !
238 function dict_get_elem( dict, key ) result(elem)
239  type(DICT_STRUCT), pointer :: dict
240  character(len=*), intent(in) :: key
241 
242  type(LINKED_LIST), pointer :: elem
243  integer :: hash
244 
245  hash = dict_hashkey( trim(key) )
246 
247  elem => dict%table(hash)%list
248  do while ( associated(elem) )
249  if ( elem%data%key .eq. key ) then
250  exit
251  else
252  elem => list_next( elem )
253  endif
254  enddo
255 end function dict_get_elem
256 
257 ! dict_hashkey
258 ! Determine the hash value from the string
259 ! Arguments:
260 ! key String to be examined
261 !
262 integer function dict_hashkey( key )
263  character(len=*), intent(in) :: key
264 
265 ! integer :: hash
266  integer :: i
267 
268  dict_hashkey = 0
269 
270  do i = 1,len(key)
271  dict_hashkey = multiplier * dict_hashkey + ichar(key(i:i))
272  enddo
273 
274  dict_hashkey = 1 + mod( dict_hashkey-1, hash_size )
275 end function dict_hashkey
276