diff --git a/src/list_header.F90 b/src/list_header.F90 index 349b9c94fa..f929fce2c2 100644 --- a/src/list_header.F90 +++ b/src/list_header.F90 @@ -8,7 +8,7 @@ module list_header ! implementation with type-bound procedures (F2003). !=============================================================================== - use constants, only: ERROR_INT, ERROR_REAL + use constants, only: ERROR_INT, ERROR_REAL, MAX_WORD_LEN implicit none @@ -26,6 +26,11 @@ module list_header type(ListElemReal), pointer :: next => null() end type ListElemReal + type :: ListElemChar + character(MAX_WORD_LEN) :: data + type(ListElemChar), pointer :: next => null() + end type ListElemChar + !=============================================================================== ! LIST* types contain the linked list with convenience methods. We originally ! considered using unlimited polymorphism to provide a single type, but compiler @@ -75,6 +80,27 @@ module list_header procedure :: size => list_size_real ! Size of list end type ListReal + type, public :: ListChar + private + integer :: count = 0 ! Number of elements in list + + ! Used in get_item for fast sequential lookups + integer :: last_index = huge(0) + type(ListElemChar), pointer :: last_elem => null() + + ! Pointers to beginning and end of list + type(ListElemChar), public, pointer :: head => null() + type(ListElemChar), public, pointer :: tail => null() + contains + procedure :: append => list_append_char ! Add item to end of list + procedure :: contains => list_contains_char ! Does list contain? + procedure :: get_item => list_get_item_char ! Get i-th item in list + procedure :: index => list_index_char ! Determine index of given item + procedure :: insert => list_insert_char ! Insert item in i-th position + procedure :: remove => list_remove_char ! Remove specified item + procedure :: size => list_size_char ! Size of list + end type ListChar + contains !=============================================================================== @@ -130,6 +156,30 @@ contains end subroutine list_append_real + subroutine list_append_char(this, data) + class(ListChar) :: this + character(*) :: data + + type(ListElemChar), pointer :: elem + + ! Create element and set dat + allocate(elem) + elem % data = data + + if (.not. associated(this % head)) then + ! If list is empty, set head and tail to new element + this % head => elem + this % tail => elem + else + ! Otherwise append element at end of list + this % tail % next => elem + this % tail => this % tail % next + end if + + this % count = this % count + 1 + + end subroutine list_append_char + !=============================================================================== ! LIST_CONTAINS determines whether the list contains a specified item. Since it ! relies on the index method, it is O(n). @@ -153,6 +203,15 @@ contains end function list_contains_real + function list_contains_char(this, data) result(in_list) + class(ListChar) :: this + character(*) :: data + logical :: in_list + + in_list = (this % index(data) > 0) + + end function list_contains_char + !=============================================================================== ! LIST_GET_ITEM returns the item in the list at position 'i_list'. If the index ! is out of bounds, an error code is returned. @@ -224,6 +283,39 @@ contains end function list_get_item_real + function list_get_item_char(this, i_list) result(data) + class(ListChar) :: this + integer :: i_list + character(MAX_WORD_LEN) :: data + + integer :: last_index + + if (i_list < 1 .or. i_list > this % count) then + ! Check for index out of bounds + data = "" + elseif (i_list == 1) then + data = this % head % data + this % last_index = 1 + this % last_elem => this % head + elseif (i_list == this % count) then + data = this % tail % data + this % last_index = this % count + this % last_elem => this % tail + else + if (i_list < this % last_index) then + this % last_index = 1 + this % last_elem => this % head + end if + + do last_index = this % last_index + 1, i_list + this % last_elem => this % last_elem % next + this % last_index = last_index + end do + data = this % last_elem % data + end if + + end function list_get_item_char + !=============================================================================== ! LIST_INDEX determines the first index in the list that contains 'data'. If ! 'data' is not present in the list, the return value is -1. @@ -271,6 +363,27 @@ contains end function list_index_real + function list_index_char(this, data) result(i_list) + + class(ListChar) :: this + character(*) :: data + integer :: i_list + + type(ListElemChar), pointer :: elem + + i_list = 0 + elem => this % head + do while (associated(elem)) + i_list = i_list + 1 + if (data == elem % data) exit + elem => elem % next + end do + + ! Check if we reached the end of the list + if (.not. associated(elem)) i_list = -1 + + end function list_index_char + !=============================================================================== ! LIST_INSERT inserts 'data' at index 'i_list' within the list. If 'i_list' ! exceeds the size of the list, the data is appends at the end of the list. @@ -366,6 +479,51 @@ contains end subroutine list_insert_real + subroutine list_insert_char(this, i_list, data) + + class(ListChar) :: this + integer :: i_list + character(*) :: data + + integer :: i + type(ListElemChar), pointer :: elem => null() + type(ListElemChar), pointer :: new_elem => null() + + if (i_list > this % count) then + ! Check whether specified index is greater than number of elements -- if + ! so, just append it to the end of the list + call this % append(data) + + else if (i_list == 1) then + ! Check for new head element + allocate(new_elem) + new_elem % data = data + new_elem % next => this % head + this % head => new_elem + this % count = this % count + 1 + + else + ! Default case with new element somewhere in middle of list + i = 0 + elem => this % head + do while (associated(elem)) + i = i + 1 + if (i == i_list - 1) then + ! Allocate new element + allocate(new_elem) + new_elem % data = data + + ! Put it before the i-th element + new_elem % next => elem % next + elem % next => new_elem + this % count = this % count + 1 + exit + end if + end do + end if + + end subroutine list_insert_char + !=============================================================================== ! LIST_REMOVE removes the first item in the list that contains 'data'. If 'data' ! is not in the list, no action is taken. @@ -449,6 +607,45 @@ contains end subroutine list_remove_real + subroutine list_remove_char(this, data) + + class(ListChar) :: this + character(*) :: data + + type(ListElemChar), pointer :: elem => null() + type(ListElemChar), pointer :: prev => null() + + elem => this % head + do while (associated(elem)) + ! Check for matching data + if (elem % data == data) then + + ! Determine whether the current element is the head, tail, or a middle + ! element + if (associated(elem, this % head)) then + this % head => elem % next + if (associated(elem, this % tail)) nullify(this % tail) + deallocate(elem) + else if (associated(elem, this % tail)) then + this % tail => prev + deallocate(this % tail % next) + else + prev % next => elem % next + deallocate(elem) + end if + + ! Decrease count and exit + this % count = this % count - 1 + exit + end if + + ! Advance pointers + prev => elem + elem => elem % next + end do + + end subroutine list_remove_char + !=============================================================================== ! LIST_SIZE returns the number of elements in the list !=============================================================================== @@ -471,4 +668,13 @@ contains end function list_size_real + function list_size_char(this) result(size) + + class(ListChar) :: this + integer :: size + + size = this % count + + end function list_size_char + end module list_header diff --git a/src/set_header.F90 b/src/set_header.F90 index a5799671a7..d6358f4178 100644 --- a/src/set_header.F90 +++ b/src/set_header.F90 @@ -11,6 +11,7 @@ module set_header ! gigantic sets where performance is critical. !=============================================================================== + use constants, only: MAX_WORD_LEN use list_header implicit none @@ -31,6 +32,17 @@ module set_header procedure :: size => set_size_int end type SetInt + type :: SetChar + private + type(ListChar) :: elements + contains + procedure :: add => set_add_char + procedure :: contains => set_contains_char + procedure :: get_item => set_get_item_char + procedure :: remove => set_remove_char + procedure :: size => set_size_char + end type SetChar + contains !=============================================================================== @@ -39,7 +51,7 @@ contains subroutine set_add_int(this, data) class(SetInt) :: this - integer :: data + integer :: data if (.not. this % elements % contains(data)) then call this % elements % append(data) @@ -47,24 +59,44 @@ contains end subroutine set_add_int + subroutine set_add_char(this, data) + class(SetChar) :: this + character(*) :: data + + if (.not. this % elements % contains(data)) then + call this % elements % append(data) + end if + + end subroutine set_add_char + !=============================================================================== ! SET_CONTAINS determines if a specified item is in a set !=============================================================================== function set_contains_int(this, data) result(in_set) class(SetInt) :: this - integer :: data - logical :: in_set + integer :: data + logical :: in_set in_set = this % elements % contains(data) end function set_contains_int + function set_contains_char(this, data) result(in_set) + class(SetChar) :: this + character(*) :: data + logical :: in_set + + in_set = this % elements % contains(data) + + end function set_contains_char + !=============================================================================== ! SET_GET_ITEM returns the i-th item in the set !=============================================================================== function set_get_item_int(this, i_list) result(data) + class(SetInt) :: this integer :: i_list integer :: data @@ -73,29 +105,59 @@ contains end function set_get_item_int + function set_get_item_char(this, i_list) result(data) + + class(SetChar) :: this + integer :: i_list + character(MAX_WORD_LEN) :: data + + data = this % elements % get_item(i_list) + + end function set_get_item_char + !=============================================================================== ! SET_REMOVE removes the specified item from the set. If it is not in the set, ! no action is taken. !=============================================================================== subroutine set_remove_int(this, data) + class(SetInt) :: this - integer :: data + integer :: data call this % elements % remove(data) end subroutine set_remove_int + subroutine set_remove_char(this, data) + + class(SetChar) :: this + character(*) :: data + + call this % elements % remove(data) + + end subroutine set_remove_char + !=============================================================================== ! SET_SIZE returns the number of elements in the set !=============================================================================== function set_size_int(this) result(size) + class(SetInt) :: this - integer :: size + integer :: size size = this % elements % size() end function set_size_int + function set_size_char(this) result(size) + + class(SetChar) :: this + integer :: size + + size = this % elements % size() + + end function set_size_char + end module set_header