MODFLOW 6  version 6.7.0.dev0
USGS Modular Hydrologic Model
List.f90
Go to the documentation of this file.
1 module listmodule
2  use kindmodule, only: dp, i4b
3  use errorutilmodule, only: pstop
4  use constantsmodule, only: linelength
8 
9  implicit none
10  private
11  public :: listtype, isequaliface
12 
13  !> @brief A generic heterogeneous doubly-linked list.
14  type :: listtype
15  ! -- Public members
16  type(listnodetype), pointer, public :: firstnode => null()
17  character(len=20), public :: name = ' '
18  ! -- Private members
19  type(listnodetype), pointer, private :: lastnode => null()
20  type(listnodetype), pointer, private :: currentnode => null()
21  integer(I4B), private :: currentnodeindex = 0
22  integer(I4B), private :: nodecount = 0
23  contains
24  ! -- Public procedures
25  procedure, public :: iterator
26  procedure, public :: add
27  procedure, public :: clear
28  procedure, public :: count
29  procedure, public :: containsobject
30  procedure, public :: deallocatebackward
31  procedure, public :: getindex
32  procedure, public :: getnextitem
33  procedure, public :: getpreviousitem
34  generic, public :: getitem => get_item_by_index, get_current_item
35  procedure, public :: insertafter
36  procedure, public :: insertbefore
37  procedure, private :: next
38  procedure, private :: previous
39  procedure, public :: reset
40  generic, public :: removenode => remove_node_by_index, remove_this_node
41  ! -- Private procedures
42  procedure, private :: get_current_item
43  procedure, private :: get_item_by_index
44  procedure, private :: get_node_by_index
45  procedure, private :: remove_node_by_index
46  procedure, private :: remove_this_node
47  ! Finalization is not supported in gfortran (as of 4.10.0)
48  !final :: clear_list
49  end type listtype
50 
51  interface
52  function isequaliface(obj1, obj2) result(isEqual)
53  class(*), pointer :: obj1, obj2
54  logical :: isequal
55  end function
56  end interface
57 
58 contains
59 
60  function iterator(this) result(itr)
61  class(listtype) :: this
62  class(iteratortype), allocatable :: itr
63 
64  itr = listiteratortype(this%firstNode)
65  end function
66 
67  !> @brief Append the given item to the list
68  subroutine add(this, objptr)
69  ! -- dummy variables
70  class(listtype), intent(inout) :: this
71  class(*), pointer, intent(inout) :: objptr
72  !
73  if (.not. associated(this%firstNode)) then
74  allocate (this%firstNode)
75  this%firstNode%Value => objptr
76  this%firstNode%prevNode => null()
77  this%lastNode => this%firstNode
78  else
79  allocate (this%lastNode%nextNode)
80  this%lastNode%nextNode%prevNode => this%lastNode
81  this%lastNode%nextNode%value => objptr
82  this%lastNode => this%lastNode%nextNode
83  end if
84  this%nodeCount = this%nodeCount + 1
85  end subroutine add
86 
87  !> @brief Deallocate all items in list
88  subroutine clear(this, destroy)
89  ! -- dummy variables
90  class(listtype) :: this
91  logical, intent(in), optional :: destroy
92  ! -- local
93  logical :: destroyLocal
94  type(listnodetype), pointer :: current => null()
95  type(listnodetype), pointer :: next => null()
96  !
97  destroylocal = .false.
98  if (present(destroy)) then
99  destroylocal = destroy
100  end if
101  !
102  if (.not. associated(this%firstNode)) return
103  ! -- The last node will be deallocated in the loop below.
104  ! Just nullify the pointer to the last node to avoid
105  ! having a dangling pointer. Also nullify currentNode.
106  nullify (this%lastNode)
107  nullify (this%currentNode)
108  !
109  current => this%firstNode
110  do while (associated(current))
111  ! -- Assign a pointer to the next node in the list
112  next => current%nextNode
113  ! -- Deallocate the object stored in the current node
114  call current%DeallocValue(destroylocal)
115  ! -- Deallocate the current node
116  deallocate (current)
117  this%firstNode => next
118  this%nodeCount = this%nodeCount - 1
119  ! -- Advance to the next node
120  current => next
121  end do
122  !
123  call this%Reset()
124 
125  end subroutine clear
126 
127  !> @brief Return number of nodes in list
128  function count(this)
129  integer(I4B) :: count
130  class(listtype) :: this
131  count = this%nodeCount
132  end function count
133 
134  !> @brief Determine whether the list contains the given object.
135  function containsobject(this, obj, isEqual) result(hasObj)
136  class(listtype), intent(inout) :: this
137  class(*), pointer :: obj
138  procedure(isequaliface), pointer, intent(in), optional :: isequal
139  logical :: hasobj
140  ! local
141  type(listnodetype), pointer :: current => null()
142 
143  hasobj = .false.
144  current => this%firstNode
145  do while (associated(current))
146  if (present(isequal)) then
147  if (isequal(current%Value, obj)) then
148  hasobj = .true.
149  return
150  end if
151  else
152  if (associated(current%Value, obj)) then
153  hasobj = .true.
154  return
155  end if
156  end if
157 
158  ! -- Advance to the next node
159  current => current%nextNode
160  end do
161 
162  end function
163 
164  !> @brief Deallocate fromNode and all previous nodes, and reassign firstNode.
165  subroutine deallocatebackward(this, fromNode)
166  ! -- dummy
167  class(listtype), target, intent(inout) :: this
168  type(listnodetype), pointer, intent(inout) :: fromNode
169  ! -- local
170  type(listnodetype), pointer :: current => null()
171  type(listnodetype), pointer :: prev => null()
172  !
173  if (associated(fromnode)) then
174  ! -- reassign firstNode
175  if (associated(fromnode%nextNode)) then
176  this%firstNode => fromnode%nextNode
177  else
178  this%firstNode => null()
179  end if
180  ! -- deallocate fromNode and all previous nodes
181  current => fromnode
182  do while (associated(current))
183  prev => current%prevNode
184  call current%DeallocValue(.true.)
185  deallocate (current)
186  this%nodeCount = this%nodeCount - 1
187  current => prev
188  end do
189  fromnode => null()
190  end if
191 
192  end subroutine deallocatebackward
193 
194  !> @brief Get the index of the given item in the list.
195  function getindex(this, obj) result(idx)
196  class(listtype), target, intent(inout) :: this
197  class(*), pointer :: obj
198  integer(I4B) :: idx
199  ! local
200  integer(I4B) :: i
201  class(*), pointer :: obj_in_list
202 
203  idx = -1
204  do i = 1, this%Count()
205  obj_in_list => this%GetItem(i)
206  if (associated(obj, obj_in_list)) then
207  idx = i
208  exit
209  end if
210  end do
211 
212  end function getindex
213 
214  !> @brief Get the next item in the list
215  function getnextitem(this) result(resultobj)
216  class(listtype), target, intent(inout) :: this
217  class(*), pointer :: resultobj
218  call this%Next()
219  resultobj => this%get_current_item()
220  end function getnextitem
221 
222  !> @brief Get the previous item in the list
223  function getpreviousitem(this) result(resultobj)
224  class(listtype), target, intent(inout) :: this
225  class(*), pointer :: resultobj
226  call this%Previous()
227  resultobj => this%get_current_item()
228  end function getpreviousitem
229 
230  !> @brief Insert the given item after the given index.
231  subroutine insertafter(this, objptr, indx)
232  ! -- dummy
233  class(listtype), intent(inout) :: this
234  class(*), pointer, intent(inout) :: objptr
235  integer(I4B), intent(in) :: indx
236  ! -- local
237  integer(I4B) :: numnodes
238  type(listnodetype), pointer :: precedingNode => null()
239  type(listnodetype), pointer :: followingNode => null()
240  type(listnodetype), pointer :: newNode => null()
241  !
242  numnodes = this%Count()
243  if (indx >= numnodes) then
244  call this%Add(objptr)
245  else
246  precedingnode => this%get_node_by_index(indx)
247  if (associated(precedingnode%nextNode)) then
248  followingnode => precedingnode%nextNode
249  allocate (newnode)
250  newnode%Value => objptr
251  newnode%nextNode => followingnode
252  newnode%prevNode => precedingnode
253  precedingnode%nextNode => newnode
254  followingnode%prevNode => newnode
255  this%nodeCount = this%nodeCount + 1
256  else
257  call pstop(1, 'Programming error in ListType%insert_after')
258  end if
259  end if
260 
261  end subroutine insertafter
262 
263  !> @brief Insert the given item before the given node.
264  subroutine insertbefore(this, objptr, targetNode)
265  ! -- dummy
266  class(listtype), intent(inout) :: this
267  class(*), pointer, intent(inout) :: objptr
268  type(listnodetype), pointer, intent(inout) :: targetNode
269  ! -- local
270  type(listnodetype), pointer :: newNode => null()
271  !
272  if (.not. associated(targetnode)) &
273  call pstop(1, 'Programming error in ListType%InsertBefore')
274  !
275  ! Allocate a new list node and point its Value member to the object
276  allocate (newnode)
277  newnode%Value => objptr
278  !
279  ! Do the insertion
280  newnode%nextNode => targetnode
281  if (associated(targetnode%prevNode)) then
282  ! Insert between two nodes
283  targetnode%prevNode%nextNode => newnode
284  newnode%prevNode => targetnode%prevNode
285  else
286  ! Insert before first node
287  this%firstNode => newnode
288  newnode%prevNode => null()
289  end if
290  targetnode%prevNode => newnode
291  this%nodeCount = this%nodeCount + 1
292 
293  end subroutine insertbefore
294 
295  !> @brief Move the list's current node pointer and index one node forwards.
296  subroutine next(this)
297  class(listtype), target, intent(inout) :: this
298 
299  if (this%currentNodeIndex == 0) then
300  if (associated(this%firstNode)) then
301  this%currentNode => this%firstNode
302  this%currentNodeIndex = 1
303  else
304  this%currentNode => null()
305  this%currentNodeIndex = 0
306  end if
307  else
308  if (associated(this%currentNode%nextNode)) then
309  this%currentNode => this%currentNode%nextNode
310  this%currentNodeIndex = this%currentNodeIndex + 1
311  else
312  this%currentNode => null()
313  this%currentNodeIndex = 0
314  end if
315  end if
316  end subroutine next
317 
318  !> @brief Move the list's current node pointer and index one node backwards.
319  subroutine previous(this)
320  class(listtype), target, intent(inout) :: this
321  if (this%currentNodeIndex <= 1) then
322  call this%Reset()
323  else
324  this%currentNode => this%currentNode%prevNode
325  this%currentNodeIndex = this%currentNodeIndex - 1
326  end if
327  end subroutine previous
328 
329  !> @brief Reset the list's current node pointer and index.
330  subroutine reset(this)
331  class(listtype), target, intent(inout) :: this
332  this%currentNode => null()
333  this%currentNodeIndex = 0
334  end subroutine reset
335 
336  !> @brief Remove the node at the given index, optionally destroying its value.
337  subroutine remove_node_by_index(this, i, destroyValue)
338  ! -- dummy
339  class(listtype), intent(inout) :: this
340  integer(I4B), intent(in) :: i
341  logical, intent(in) :: destroyValue
342  ! -- local
343  type(listnodetype), pointer :: node
344  !
345  node => null()
346  node => this%get_node_by_index(i)
347  if (associated(node)) then
348  call this%remove_this_node(node, destroyvalue)
349  end if
350 
351  end subroutine remove_node_by_index
352 
353  !> @brief Remove the given node, optionally destroying its value.
354  subroutine remove_this_node(this, node, destroyValue)
355  ! -- dummy
356  class(listtype), intent(inout) :: this
357  type(listnodetype), pointer, intent(inout) :: node
358  logical, intent(in) :: destroyValue
359  ! -- local
360  !
361  logical :: first, last
362  !
363  first = .false.
364  last = .false.
365  if (associated(node)) then
366  if (associated(node%prevNode)) then
367  if (associated(node%nextNode)) then
368  node%nextNode%prevNode => node%prevNode
369  else
370  node%prevNode%nextNode => null()
371  this%lastNode => node%prevNode
372  end if
373  else
374  first = .true.
375  end if
376  if (associated(node%nextNode)) then
377  if (associated(node%prevNode)) then
378  node%prevNode%nextNode => node%nextNode
379  else
380  node%nextNode%prevNode => null()
381  this%firstNode => node%nextNode
382  end if
383  else
384  last = .true.
385  end if
386  if (destroyvalue) then
387  call node%DeallocValue(destroyvalue)
388  end if
389  deallocate (node)
390  this%nodeCount = this%nodeCount - 1
391  if (first .and. last) then
392  this%firstNode => null()
393  this%lastNode => null()
394  this%currentNode => null()
395  end if
396  call this%Reset()
397  end if
398 
399  end subroutine remove_this_node
400 
401  ! -- Private type-bound procedures for ListType
402 
403  !> @brief Get a pointer to the item at the current node.
404  function get_current_item(this) result(resultobj)
405  class(listtype), target, intent(inout) :: this
406  ! result
407  class(*), pointer :: resultobj
408  !
409  resultobj => null()
410  if (associated(this%currentNode)) then
411  resultobj => this%currentNode%Value
412  end if
413  end function get_current_item
414 
415  !> @brief Get a pointer to the item at the given index.
416  function get_item_by_index(this, indx) result(resultobj)
417  ! -- dummy
418  class(listtype), intent(inout) :: this
419  integer(I4B), intent(in) :: indx
420  ! result
421  class(*), pointer :: resultobj
422  ! -- local
423  integer(I4B) :: i
424  !
425  ! -- Initialize
426  resultobj => null()
427  !
428  ! -- Ensure that this%currentNode is associated
429  if (.not. associated(this%currentNode)) then
430  this%currentNodeIndex = 0
431  end if
432  if (this%currentNodeIndex == 0) then
433  if (associated(this%firstNode)) then
434  this%currentNode => this%firstNode
435  this%currentNodeIndex = 1
436  end if
437  end if
438  !
439  ! -- Check indx position relative to current node index
440  i = 0
441  if (indx < this%currentNodeIndex) then
442  ! Start at beginning of list
443  call this%Reset()
444  if (associated(this%firstNode)) then
445  this%currentNode => this%firstNode
446  this%currentNodeIndex = 1
447  i = 1
448  end if
449  else
450  i = this%currentNodeIndex
451  end if
452  if (i == 0) return
453  !
454  ! -- If current node is requested node,
455  ! assign pointer and return
456  if (i == indx) then
457  resultobj => this%currentNode%Value
458  return
459  end if
460  !
461  ! -- Iterate from current node to requested node
462  do while (associated(this%currentNode%nextNode))
463  this%currentNode => this%currentNode%nextNode
464  this%currentNodeIndex = this%currentNodeIndex + 1
465  if (this%currentNodeIndex == indx) then
466  resultobj => this%currentNode%Value
467  return
468  end if
469  end do
470  end function get_item_by_index
471 
472  !> @brief Get the node at the given index
473  function get_node_by_index(this, indx) result(resultnode)
474  ! -- dummy
475  class(listtype), intent(inout) :: this
476  integer(I4B), intent(in) :: indx
477  ! result
478  type(listnodetype), pointer :: resultnode
479  ! -- local
480  integer(I4B) :: i
481  !
482  ! -- Initialize
483  resultnode => null()
484  !
485  ! -- Ensure that this%currentNode is associated
486  if (this%currentNodeIndex == 0) then
487  if (associated(this%firstNode)) then
488  this%currentNode => this%firstNode
489  this%currentNodeIndex = 1
490  end if
491  end if
492  !
493  ! -- Check indx position relative to current node index
494  i = 0
495  if (indx < this%currentNodeIndex) then
496  ! Start at beginning of list
497  call this%Reset()
498  if (associated(this%firstNode)) then
499  this%currentNode => this%firstNode
500  this%currentNodeIndex = 1
501  i = 1
502  end if
503  else
504  i = this%currentNodeIndex
505  end if
506  if (i == 0) return
507  !
508  ! -- If current node is requested node,
509  ! assign pointer and return
510  if (i == indx) then
511  resultnode => this%currentNode
512  return
513  end if
514  !
515  ! -- Iterate from current node to requested node
516  do while (associated(this%currentNode%nextNode))
517  this%currentNode => this%currentNode%nextNode
518  this%currentNodeIndex = this%currentNodeIndex + 1
519  if (this%currentNodeIndex == indx) then
520  resultnode => this%currentNode
521  return
522  end if
523  end do
524  end function get_node_by_index
525 
526 end module listmodule
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
subroutine pstop(status, message)
Stop the program, optionally specifying an error status code.
Definition: ErrorUtil.f90:24
This module defines variable data types.
Definition: kind.f90:8
integer(i4b) function count(this)
Return number of nodes in list.
Definition: List.f90:129
subroutine insertbefore(this, objptr, targetNode)
Insert the given item before the given node.
Definition: List.f90:265
subroutine remove_node_by_index(this, i, destroyValue)
Remove the node at the given index, optionally destroying its value.
Definition: List.f90:338
subroutine reset(this)
Reset the list's current node pointer and index.
Definition: List.f90:331
class(*) function, pointer getpreviousitem(this)
Get the previous item in the list.
Definition: List.f90:224
class(*) function, pointer get_item_by_index(this, indx)
Get a pointer to the item at the given index.
Definition: List.f90:417
logical function containsobject(this, obj, isEqual)
Determine whether the list contains the given object.
Definition: List.f90:136
subroutine add(this, objptr)
Append the given item to the list.
Definition: List.f90:69
subroutine clear(this, destroy)
Deallocate all items in list.
Definition: List.f90:89
class(*) function, pointer get_current_item(this)
Get a pointer to the item at the current node.
Definition: List.f90:405
subroutine previous(this)
Move the list's current node pointer and index one node backwards.
Definition: List.f90:320
subroutine next(this)
Move the list's current node pointer and index one node forwards.
Definition: List.f90:297
integer(i4b) function getindex(this, obj)
Get the index of the given item in the list.
Definition: List.f90:196
class(*) function, pointer getnextitem(this)
Get the next item in the list.
Definition: List.f90:216
class(iteratortype) function, allocatable iterator(this)
Definition: List.f90:61
subroutine deallocatebackward(this, fromNode)
Deallocate fromNode and all previous nodes, and reassign firstNode.
Definition: List.f90:166
type(listnodetype) function, pointer get_node_by_index(this, indx)
Get the node at the given index.
Definition: List.f90:474
subroutine remove_this_node(this, node, destroyValue)
Remove the given node, optionally destroying its value.
Definition: List.f90:355
subroutine insertafter(this, objptr, indx)
Insert the given item after the given index.
Definition: List.f90:232
An iterator used to iterate through a List.
A generic heterogeneous doubly-linked list.
Definition: List.f90:14