LCOV - code coverage report
Current view: top level - utils/private - utils_idict.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 65.3 % 242 158
Test Date: 2025-05-08 18:23:42 Functions: 66.7 % 18 12

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2014  The MESA Team
       4              : !
       5              : !   This program is free software: you can redistribute it and/or modify
       6              : !   it under the terms of the GNU Lesser General Public License
       7              : !   as published by the Free Software Foundation,
       8              : !   either version 3 of the License, or (at your option) any later version.
       9              : !
      10              : !   This program is distributed in the hope that it will be useful,
      11              : !   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12              : !   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13              : !   See the GNU Lesser General Public License for more details.
      14              : !
      15              : !   You should have received a copy of the GNU Lesser General Public License
      16              : !   along with this program. If not, see <https://www.gnu.org/licenses/>.
      17              : !
      18              : ! ***********************************************************************
      19              : 
      20              :       module utils_idict
      21              : 
      22              :       use utils_def, only: integer_idict, ihash_entry
      23              : 
      24              :       implicit none
      25              : 
      26              :       contains
      27              : 
      28            0 :       recursive subroutine do_integer_idict_map(idict, fcn, ierr)
      29              :          type (integer_idict), pointer :: idict
      30              :          interface
      31              :             subroutine fcn(key1, key2, value, ierr)
      32              :                implicit none
      33              :                integer, intent(in) :: key1, key2, value
      34              :                integer, intent(out) :: ierr  ! /= 0 means terminate map calls
      35              :             end subroutine fcn
      36              :          end interface
      37              :          type (integer_idict), pointer :: node
      38              :          integer, intent(out) :: ierr
      39            0 :          ierr = 0
      40            0 :          if (.not. associated(idict)) return
      41              :          node => idict
      42            0 :          do
      43            0 :             if (associated(node% left)) then
      44            0 :                call do_integer_idict_map(node% left, fcn, ierr)
      45            0 :                if (ierr /= 0) return
      46              :             end if
      47            0 :             call fcn(node% key1, node% key2, node% value, ierr)
      48            0 :             if (ierr /= 0) return
      49            0 :             if (.not. associated(node% right)) return
      50              :             node => node% right
      51              :          end do
      52              :       end subroutine do_integer_idict_map
      53              : 
      54              : 
      55            0 :       subroutine do_get_idict_entries(idict, key1s, key2s, values)
      56              :          type (integer_idict), pointer :: idict
      57              :          integer, pointer, dimension(:) :: key1s, key2s, values
      58              : 
      59              :          integer :: cnt, ierr, sz
      60            0 :          sz = size_integer_idict(idict)
      61            0 :          sz = min(sz, size(key1s,dim=1), size(key2s,dim=1), size(values,dim=1))
      62            0 :          cnt = 0
      63            0 :          call do_integer_idict_map(idict, fcn, ierr)
      64              : 
      65              :          contains
      66              : 
      67            0 :          subroutine fcn(key1, key2, value, ierr)
      68              :             integer, intent(in) :: key1, key2, value
      69              :             integer, intent(out) :: ierr  ! /= 0 means terminate map calls
      70            0 :             if (cnt >= sz) then
      71            0 :                ierr = -1
      72            0 :                return
      73              :             end if
      74            0 :             cnt = cnt+1
      75            0 :             key1s(cnt) = key1
      76            0 :             key2s(cnt) = key2
      77            0 :             values(cnt) = value
      78              :          end subroutine fcn
      79              : 
      80              :       end subroutine do_get_idict_entries
      81              : 
      82              : 
      83            0 :       recursive subroutine show_key1_key2_entries(idict)
      84              :          type (integer_idict), pointer :: idict
      85              :          type (integer_idict), pointer :: node
      86            0 :          if (.not. associated(idict)) return
      87              :          node => idict
      88              :          do
      89            0 :             if (associated(node% left)) then
      90            0 :                call show_key1_key2_entries(node% left)
      91              :             end if
      92            0 :             write(*,fmt='(3i10)') node% key1, node% key2, node% value
      93            0 :             if (.not. associated(node% right)) return
      94              :             node => node% right
      95              :          end do
      96              :       end subroutine show_key1_key2_entries
      97              : 
      98              : 
      99            4 :       subroutine find_key1_key2_entry(idict, key1, key2, node)
     100              :          type (integer_idict), pointer :: idict
     101              :          integer, intent(in) :: key1, key2
     102              :          type (integer_idict), pointer :: node  ! set null if cannot find key1, key2 in idict
     103            4 :          type (ihash_entry), pointer :: hash(:)
     104              :          integer :: i, hash_size, hashkey
     105            4 :          if (.not. associated(idict)) then
     106            0 :             nullify(node); return
     107              :          end if
     108            4 :          if (associated(idict% hash)) then
     109            4 :             hash => idict% hash
     110            4 :             hash_size = size(hash)
     111            4 :             hashkey = idict_hashkey(key1, key2, hash_size)
     112            4 :             do i=1, hash_size  ! find an empty slot
     113            4 :                if (.not. associated(hash(hashkey)% ptr)) exit
     114            3 :                if (hash(hashkey)% ptr% key1 == key1 .and. &
     115              :                    hash(hashkey)% ptr% key2 == key2) then
     116            3 :                   node => hash(hashkey)% ptr
     117            3 :                   return
     118              :                end if
     119            0 :                hashkey = hashkey+1
     120            1 :                if (hashkey > hash_size) hashkey = 1
     121              :             end do
     122            1 :             nullify(node)
     123            1 :             return  ! failed to find key1, key2
     124              :          end if
     125            0 :          node => idict
     126              :          do
     127            0 :             if (node% key1 == key1 .and. node% key2 == key2) return
     128            0 :             if (node% key1 < key1 .or. &
     129              :                   (node% key1 == key1 .and. node% key2 < key2)) then
     130            0 :                if (.not. associated(node% left)) then
     131            0 :                   nullify(node); return
     132              :                end if
     133            0 :                node => node% left
     134              :             else
     135            0 :                if (.not. associated(node% right)) then
     136            0 :                   nullify(node); return
     137              :                end if
     138            0 :                node => node% right
     139              :             end if
     140              :          end do
     141            4 :       end subroutine find_key1_key2_entry
     142              : 
     143              : 
     144           11 :       recursive subroutine insert_node(node, root, duplicate)
     145              :          type (integer_idict), pointer :: node  ! will be deallocated if a duplicate
     146              :          type (integer_idict), pointer :: root
     147              :          logical :: duplicate  ! true if key was already defined
     148              : 
     149              :          integer :: height_left, height_right
     150              :          logical, parameter :: dbg = .false.
     151              : 
     152           11 :          if (node% key1 == root% key1 .and. node% key2 == root% key2) then
     153            3 :             root% value = node% value
     154            3 :             deallocate(node)
     155              :             nullify(node)
     156            3 :             duplicate = .true.
     157            3 :             return
     158              :          end if
     159              : 
     160            8 :          if (node% key1 > root% key1 .or. &
     161              :             (node% key1 == root% key1 .and. &
     162              :              node% key2 > root% key2)) then  ! insert on left
     163            2 :             if (.not. associated(root% left)) then
     164            1 :                root% left => node
     165              :             else
     166            1 :                call insert_node(node, root% left, duplicate)
     167              :             end if
     168            2 :             height_left = root% left% height
     169            2 :             height_right = height_of_right_branch(root)
     170            2 :             if (height_left - height_right == 2) then  ! rebalance
     171            1 :                if (node% key1 > root% left% key1 .or. &
     172              :                   (node% key1 == root% left% key1 .and. &
     173              :                    node% key2 > root% left% key2)) then  ! insert on left
     174            0 :                   call single_rotate_with_left(root)
     175              :                else
     176            1 :                   call double_rotate_with_left(root)
     177              :                end if
     178              :             end if
     179              :          else  ! insert on right
     180            6 :             if (.not. associated(root% right)) then
     181            2 :                root% right => node
     182              :             else
     183            4 :                call insert_node(node, root% right, duplicate)
     184              :             end if
     185            6 :             height_right = root% right% height
     186            6 :             height_left = height_of_left_branch(root)
     187            6 :             if (height_right - height_left == 2) then  ! rebalance
     188            0 :                if (root% right% key1 > node% key1 .or. &
     189              :                   (root% right% key1 == node% key1 .and. &
     190              :                    root% right% key2 > node% key2)) then
     191            0 :                   call single_rotate_with_right(root)
     192              :                else
     193            0 :                   call double_rotate_with_right(root)
     194              :                end if
     195              :             end if
     196              :          end if
     197              : 
     198            8 :          height_right = height_of_right_branch(root)
     199            8 :          height_left = height_of_left_branch(root)
     200            8 :          root% height = max(height_right, height_left) + 1
     201              : 
     202              : 
     203              :          contains
     204              : 
     205              : 
     206           17 :          integer function height_of_left_branch(n)
     207              :             type (integer_idict), pointer :: n
     208           14 :             if (.not. associated(n% left)) then
     209              :                height_of_left_branch = 0
     210              :             else
     211            9 :                height_of_left_branch = n% left% height
     212              :             end if
     213              :          end function height_of_left_branch
     214              : 
     215              : 
     216           13 :          integer function height_of_right_branch(n)
     217              :             type (integer_idict), pointer :: n
     218           10 :             if (.not. associated(n% right)) then
     219              :                height_of_right_branch = 0
     220              :             else
     221            7 :                height_of_right_branch = n% right% height
     222              :             end if
     223              :          end function height_of_right_branch
     224              : 
     225              : 
     226            1 :          subroutine single_rotate_with_left(k2)
     227              :             type (integer_idict), pointer :: k2
     228              :             type (integer_idict), pointer :: k1
     229            1 :             k1 => k2% left
     230            1 :             if (.not. associated(k1% right)) then
     231            1 :                nullify(k2% left)
     232              :             else
     233            0 :                k2% left => k1% right
     234              :             end if
     235            1 :             k1% right => k2
     236            1 :             k2% height = max(height_of_left_branch(k2), height_of_right_branch(k2)) + 1
     237            1 :             k1% height = max(height_of_left_branch(k1), k2% height) + 1
     238            1 :             k2 => k1
     239            1 :          end subroutine single_rotate_with_left
     240              : 
     241              : 
     242            1 :          subroutine single_rotate_with_right(k1)
     243              :             type (integer_idict), pointer :: k1
     244              :             type (integer_idict), pointer :: k2
     245            1 :             k2 => k1% right
     246            1 :             if (.not. associated(k2% left)) then
     247            1 :                nullify(k1% right)
     248              :             else
     249            0 :                k1% right => k2% left
     250              :             end if
     251            1 :             k2% left => k1
     252            1 :             k1% height = max(height_of_right_branch(k1), height_of_left_branch(k1)) + 1
     253            1 :             k2% height = max(height_of_right_branch(k2), k1% height) + 1
     254            1 :             k1 => k2
     255            1 :          end subroutine single_rotate_with_right
     256              : 
     257              : 
     258            1 :          subroutine double_rotate_with_left(k)
     259              :             type (integer_idict), pointer :: k
     260            1 :             call single_rotate_with_right(k% left)
     261            1 :             call single_rotate_with_left(k)
     262            1 :          end subroutine double_rotate_with_left
     263              : 
     264              : 
     265            0 :          subroutine double_rotate_with_right(k)
     266              :             type (integer_idict), pointer :: k
     267            0 :             call single_rotate_with_left(k% right)
     268            0 :             call single_rotate_with_right(k)
     269            0 :          end subroutine double_rotate_with_right
     270              : 
     271              : 
     272              :       end subroutine insert_node
     273              : 
     274              : 
     275            7 :       subroutine do_integer_idict_define(idict, key1, key2, value, duplicate, ierr)
     276              :          type (integer_idict), pointer :: idict  ! pass null for empty idict
     277              :          integer, intent(in) :: key1, key2, value
     278              :          logical, intent(out) :: duplicate  ! true if key was already defined
     279              :          integer, intent(out) :: ierr
     280              :          type (integer_idict), pointer :: node
     281              :          logical, parameter :: dbg = .false.
     282              :          ierr = 0
     283            7 :          allocate(node, stat=ierr)
     284            7 :          if (ierr /= 0) return
     285            7 : !$omp critical (idict_define)
     286            7 :          duplicate = .false.
     287            7 :          node% key1 = key1
     288            7 :          node% key2 = key2
     289            7 :          node% value = value
     290            7 :          node% height = 1
     291            7 :          nullify(node% left)
     292            7 :          nullify(node% right)
     293            7 :          nullify(node% hash)
     294            7 :          if (.not. associated(idict)) then  ! this is the 1st entry
     295            1 :             idict => node
     296              :          else
     297            6 :             if (associated(idict% hash)) then
     298            0 :                deallocate(idict% hash)
     299            0 :                nullify(idict% hash)
     300              :             end if
     301            6 :             call insert_node(node, idict, duplicate)
     302              :          end if
     303              : !$omp end critical (idict_define)
     304              :          if (dbg) then  ! check tree
     305              :             write(*,'(A)')
     306              :             call check_idict(idict, ierr)
     307              :             call show_key1_key2_entries(idict)
     308              :             write(*,*) 'done insert', key1, key2
     309              :          end if
     310              :       end subroutine do_integer_idict_define
     311              : 
     312              : 
     313            5 :       subroutine do_integer_idict_create_hash(idict, ierr)
     314              :          type (integer_idict), pointer :: idict
     315              :          integer, intent(out) :: ierr
     316              : 
     317              :          integer :: cnt, hash_size, i, collisions
     318            5 :          type (ihash_entry), pointer :: hash(:)
     319              : 
     320            5 :          ierr = 0
     321            5 :          if (.not. associated(idict)) then
     322            4 :             ierr = -1; return
     323              :          end if
     324            5 :          if (associated(idict% hash)) return
     325              : 
     326            1 : !$omp critical (create_hash)
     327            1 :          if (.not. associated(idict% hash)) then
     328            1 :             cnt = size_integer_idict(idict)  ! number of entries
     329            1 :             if (cnt > 0) then
     330            1 :                hash_size = 4*cnt
     331            1 :                allocate(idict% hash(hash_size), stat=ierr)
     332            1 :                if (ierr /= 0) then
     333            0 :                   write(*,*) 'failed in allocate for create hash', hash_size
     334              :                else
     335            1 :                   hash => idict% hash
     336           17 :                   do i=1,hash_size
     337           17 :                      nullify(hash(i)% ptr)
     338              :                   end do
     339            1 :                   collisions = 0
     340            1 :                   call do_enter_hash(idict, hash, hash_size, collisions)
     341              :                end if
     342              :             end if
     343              :          end if
     344              : !$omp end critical (create_hash)
     345              : 
     346            5 :       end subroutine do_integer_idict_create_hash
     347              : 
     348              : 
     349            0 :       recursive subroutine check_idict(idict, ierr)
     350              :          type (integer_idict), pointer :: idict
     351              :          integer, intent(out) :: ierr
     352              :          integer :: height_left, height_right, height
     353            0 :          if (associated(idict% left)) then
     354            0 :             if (idict% key1 > idict% left% key1 .or. &
     355              :                (idict% key1 == idict% left% key1 .and. &
     356              :                 idict% key2 > idict% left% key2)) then
     357            0 :                write(*,*) 'wrong order idict% key1, key2, idict% left% key1, key2', &
     358            0 :                    idict% key1, idict% key2, idict% left% key1, idict% left% key2
     359            0 :                ierr = -1
     360            0 :                return
     361              :             end if
     362            0 :             call check_idict(idict% left, ierr)
     363            0 :             if (ierr /= 0) return
     364            0 :             height_left = idict% left% height
     365              :          else
     366              :             height_left = 0
     367              :          end if
     368            0 :          if (associated(idict% right)) then
     369            0 :             if (idict% right% key1 > idict% key1 .or. &
     370              :                (idict% right% key1 == idict% key1 .and. &
     371              :                 idict% right% key2 > idict% key2)) then
     372            0 :                write(*,*) 'wrong order idict% right% key1, key2, idict% key1, key2', &
     373            0 :                    idict% right% key1, idict% right% key2, idict% key1, idict% key2
     374            0 :                ierr = -1
     375            0 :                return
     376              :             end if
     377            0 :             call check_idict(idict% right, ierr)
     378            0 :             if (ierr /= 0) return
     379            0 :             height_right = idict% right% height
     380              :          else
     381              :             height_right = 0
     382              :          end if
     383            0 :          height = max(height_left, height_right) + 1
     384            0 :          if (idict% height /= height) then
     385            0 :             write(*,*) 'bad height for', idict% key1, idict% key2
     386            0 :             ierr = -1
     387              :          end if
     388              :       end subroutine check_idict
     389              : 
     390              : 
     391            4 :       subroutine do_integer_idict_lookup(idict, key1, key2, value, ierr)
     392              :          type (integer_idict), pointer :: idict
     393              :          integer, intent(in) :: key1, key2
     394              :          integer, intent(out) :: value
     395              :          integer, intent(out) :: ierr  ! 0 if found key1, key2 in idict, -1 if didn't
     396              :          type (integer_idict), pointer :: node
     397              :          logical, parameter :: dbg = .false.
     398              :          if (dbg) then
     399              :             call show_key1_key2_entries(idict)
     400              :             write(*,'(A)')
     401              :             write(*,*) 'lookup key1, key2', key1, key2
     402              :             write(*,'(A)')
     403              :          end if
     404              :          ierr = 0
     405            4 :          value = 0
     406            4 :          call do_integer_idict_create_hash(idict, ierr)
     407            7 :          if (ierr /= 0) return
     408            4 :          call find_key1_key2_entry(idict, key1, key2, node)
     409            4 :          if (associated(node)) then
     410            3 :             value = node% value
     411            3 :             return
     412              :          end if
     413            1 :          ierr = -1
     414              :       end subroutine do_integer_idict_lookup
     415              : 
     416              : 
     417            2 :       recursive subroutine do_integer_idict_free(idict)
     418              :          type (integer_idict), pointer :: idict
     419              :          type (integer_idict), pointer :: node, next
     420            2 :          if (.not. associated(idict)) return
     421            2 :          node => idict
     422            2 :          if (associated(node% hash)) deallocate(node% hash)
     423            2 :          do
     424            4 :             if (associated(node% left)) call do_integer_idict_free(node% left)
     425            4 :             if (.not. associated(node% right)) then
     426            2 :                deallocate(node)
     427            2 :                return
     428              :             end if
     429            2 :             next => node% right
     430            2 :             deallocate(node)
     431            2 :             node => next
     432              :          end do
     433              :       end subroutine do_integer_idict_free
     434              : 
     435              : 
     436            2 :       recursive function size_integer_idict(idict) result(cnt)
     437              :          type (integer_idict), pointer :: idict
     438              :          type (integer_idict), pointer :: node, next
     439              :          integer :: cnt
     440            2 :          cnt = 0
     441            2 :          if (.not. associated(idict)) return
     442              :          node => idict
     443              :          do
     444            4 :             cnt = cnt + 1
     445            4 :             if (associated(node% left)) cnt = cnt + size_integer_idict(node% left)
     446            4 :             if (.not. associated(node% right)) return
     447              :             next => node% right
     448              :             node => next
     449              :          end do
     450              :       end function size_integer_idict
     451              : 
     452              : 
     453            2 :       recursive subroutine do_enter_hash(idict, hash, hash_size, collisions)
     454              :          type (integer_idict), pointer :: idict
     455              :          type (ihash_entry), pointer :: hash(:)
     456              :          integer, intent(in) :: hash_size
     457              :          integer, intent(inout) :: collisions
     458              :          type (integer_idict), pointer :: node, next
     459              :          integer :: hashkey, i
     460              :          logical :: okay
     461            2 :          if (.not. associated(idict)) return
     462              :          node => idict
     463              :          do
     464              :             ! enter node in hash
     465            4 :             hashkey = idict_hashkey(node% key1, node% key2, hash_size)
     466            4 :             okay = .false.
     467            4 :             do i=1, hash_size  ! find an empty slot
     468            4 :                if (.not. associated(hash(hashkey)% ptr)) then
     469            4 :                   hash(hashkey)% ptr => node
     470              :                   okay = .true.
     471              :                   exit
     472              :                end if
     473            0 :                hashkey = hashkey+1
     474            0 :                collisions = collisions+1
     475            0 :                if (hashkey > hash_size) hashkey = 1
     476              :             end do
     477              :             if (.not. okay) then
     478            0 :                write(*,*) 'failed in do_enter_hash'
     479            0 :                error stop 1
     480              :             end if
     481            4 :             if (associated(node% left)) &
     482            1 :                call do_enter_hash(node% left, hash, hash_size, collisions)
     483            4 :             if (.not. associated(node% right)) return
     484              :             next => node% right
     485              :             node => next
     486              :          end do
     487              :       end subroutine do_enter_hash
     488              : 
     489              : 
     490            8 :       integer function idict_hashkey(key1, key2, hash_size)  ! value between 1 and hash_size
     491              :          integer, intent(in) :: key1, key2, hash_size
     492              :          integer:: new, hash, c
     493              :          ! source: http://www.partow.net/programming/hashfunctions/#APHashFunction
     494            8 :          hash = -1431655766  ! Z'AAAAAAAA'
     495            8 :          c = key1
     496            8 :          if (iand(c,1)==1) then
     497              :             !new = (hash <<  7) ^ (*str) * (hash >> 3)
     498            4 :             new = ieor(ishft(hash,7), c*ishft(hash,3))
     499              :          else
     500              :             !new = ~((hash << 11) + (*str) ^ (hash >> 5))
     501            4 :             new = not(ishft(hash,11) + ieor(c,ishft(hash,-5)))
     502              :          end if
     503            8 :          hash = ieor(hash, new)
     504            8 :          c = key2
     505            8 :          if (iand(c,1)==1) then
     506              :             !new = (hash <<  7) ^ (*str) * (hash >> 3)
     507            2 :             new = ieor(ishft(hash,7), c*ishft(hash,3))
     508              :          else
     509              :             !new = ~((hash << 11) + (*str) ^ (hash >> 5))
     510            6 :             new = not(ishft(hash,11) + ieor(c,ishft(hash,-5)))
     511              :          end if
     512            8 :          idict_hashkey = ieor(hash, new)
     513            8 :          if (idict_hashkey < 0) then
     514            6 :             idict_hashkey = -idict_hashkey
     515            2 :          else if (idict_hashkey == 0) then
     516            0 :             idict_hashkey = 1
     517              :          end if
     518            8 :          idict_hashkey = 1 + mod(idict_hashkey-1, hash_size)
     519            8 :          if (idict_hashkey <= 0) then
     520            0 :             write(*,*) 'bad idict_hashkey for', key1, key2, idict_hashkey
     521            0 :             stop 'idict_hashkey'
     522              :          end if
     523            8 :       end function idict_hashkey
     524              : 
     525              :       end module utils_idict
        

Generated by: LCOV version 2.0-1