Skip to content

Hash maps #611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 92 commits into from
Jun 20, 2022
Merged
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
24e3fbb
Started creation of the stdlib_hash_maps PR
wclodius2 Dec 27, 2021
e0339c6
Fixed typos
wclodius2 Dec 27, 2021
bf97fed
Simplified hash maps
wclodius2 Dec 27, 2021
fade537
Extensive rewrite
wclodius2 Dec 28, 2021
ab8b302
Changed the names of three modules in documentation
wclodius2 Dec 29, 2021
f0bc292
Revised first paragraph
wclodius2 Dec 29, 2021
ce445ed
Revised module name
wclodius2 Dec 29, 2021
24e7154
Improved documentation of procedures
wclodius2 Dec 29, 2021
370019d
Better documented inmap
wclodius2 Dec 30, 2021
33647a0
Changed example
wclodius2 Dec 30, 2021
c386c36
Fixed typos for hasher_fun documentation
wclodius2 Dec 30, 2021
791607e
Changed name of equality test function
wclodius2 Dec 30, 2021
58827e6
Commited suggestions of Jeremie
wclodius2 Dec 30, 2021
5e11c7e
More changes suggested by Jereemie
wclodius2 Dec 30, 2021
7dd1a51
More changes suggested by Jeremie
wclodius2 Dec 31, 2021
60addf4
Fixed typos
wclodius2 Dec 31, 2021
0d0669e
Simplified discussion of fibonacci_hash
wclodius2 Dec 31, 2021
0284dee
Merge branch 'master' of https://github.com/fortran-lang/stdlib into …
wclodius2 Jan 2, 2022
4bb3dac
Changed documented structure of `other_type`
wclodius2 Jan 2, 2022
9f3471d
Document equality and conversion to inheritence
wclodius2 Jan 7, 2022
c9f2d6c
Fixed typos
wclodius2 Jan 10, 2022
30b0be3
Added documentation for proposed new API
wclodius2 Jan 10, 2022
9c7c0cf
Fixed misspelling
wclodius2 Jan 10, 2022
b30b257
Fixed misspelling
wclodius2 Jan 10, 2022
21886cd
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
48ba158
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
171029e
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
106cefd
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
5f003d7
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
a950ef5
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
e6b8f54
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
8f94482
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
b17830d
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
e622c4f
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
d7b90c9
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
d4c7487
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
983a077
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
cf89d00
Update doc/specs/stdlib_hashmaps.md
wclodius2 Jan 12, 2022
cb5a6df
Changed (TOC) to [TOC]
wclodius2 Jan 12, 2022
b40bf3a
Merge branch 'hash_maps' of github.com:wclodius2/stdlib into hash_maps
wclodius2 Jan 12, 2022
2782d3c
Fixed typos
wclodius2 Jan 12, 2022
396b6f6
Various changes
wclodius2 Jan 12, 2022
fdd7c92
Fixed typos
wclodius2 Jan 16, 2022
c0fdd91
Improved descriptions
wclodius2 Jan 17, 2022
472eeef
Minor error corrections
wclodius2 Jan 18, 2022
ede7f79
Merge branch 'master' of https://github.com/fortran-lang/stdlib into …
wclodius2 Jan 19, 2022
f4c8c1c
Made spelling of procedure consistent
wclodius2 Jan 23, 2022
5053777
Changed the function "valid_key" to the subroutine "key_test"
wclodius2 Jan 25, 2022
69c834c
Merge branch 'master' of https://github.com/fortran-lang/stdlib into …
wclodius2 Jan 27, 2022
265f37d
Merge branch 'master' of https://github.com/fortran-lang/stdlib into …
wclodius2 Mar 3, 2022
46ba99e
Deleted file
wclodius2 Mar 6, 2022
c43a5c7
Added hashmaps to the index
wclodius2 Mar 6, 2022
42f3f12
Added hashmap source files
wclodius2 Mar 15, 2022
2741106
Added hashmaps test files
wclodius2 Mar 15, 2022
a98a1d7
Corrected typos and ensured no trailing whitespace.
wclodius2 Mar 15, 2022
4ced800
Corrected misspellings
wclodius2 Mar 15, 2022
4eb70ff
Fixed misspelling of a file
wclodius2 Mar 15, 2022
3a935cf
Fixed Makefile.manual
wclodius2 Mar 15, 2022
a567329
Changed "and" to "an"
wclodius2 Mar 15, 2022
b91ff35
Merge branch 'master' of https://github.com/fortran-lang/stdlib into …
wclodius2 Mar 30, 2022
67e63d3
Update doc/specs/stdlib_hashmaps.md
wclodius2 Apr 24, 2022
a169c65
Update doc/specs/stdlib_hashmaps.md
wclodius2 Apr 24, 2022
16b2227
Update src/stdlib_hashmap_wrappers.f90
wclodius2 Apr 24, 2022
f6c2b26
Merge branch 'master' of https://github.com/fortran-lang/stdlib into …
wclodius2 Apr 24, 2022
32a1d39
Merge branch 'hash_maps' of github.com:wclodius2/stdlib into hash_maps
wclodius2 Apr 24, 2022
6da72d4
Update doc/specs/stdlib_hashmaps.md
wclodius2 May 5, 2022
421daa6
Merge branch 'hash_maps' of github.com:wclodius2/stdlib into hash_maps
wclodius2 May 9, 2022
d161453
Update src/stdlib_hashmap_wrappers.f90
wclodius2 May 9, 2022
85dad9d
Update src/stdlib_hashmap_wrappers.f90
wclodius2 May 9, 2022
8ae3263
Update src/stdlib_hashmap_chaining.f90
wclodius2 May 9, 2022
fd15686
fixed issue with string escaping in "can't"
14NGiestas May 9, 2022
0f64bc0
fixed issue with line being too long in error stop
14NGiestas May 9, 2022
9c812d8
Merge branch 'hash_maps' of github.com:wclodius2/stdlib into hash_maps
wclodius2 May 9, 2022
dcfb087
Made changes in stdlib_hashmaps.md suggested by Ian Gaestrus Pauli an…
wclodius2 May 9, 2022
5d6741d
Changed api of set for keys
wclodius2 May 9, 2022
21c9fc4
Corrected the use of set for keys in examples
wclodius2 May 9, 2022
817633f
Improved error messages
wclodius2 May 10, 2022
080d719
Fixed carriage return
wclodius2 May 10, 2022
ea7d80f
Fixed string in error message
wclodius2 May 10, 2022
ede1534
Improved error reporting
wclodius2 May 10, 2022
b2276b7
Changed error reporting for in_chain_map.
wclodius2 May 11, 2022
5fa8bb2
Merge branch 'master' of https://github.com/fortran-lang/stdlib into …
wclodius2 May 23, 2022
c4d4ec7
change proposed to specs
jvdp1 Jun 17, 2022
b4c0b75
Update doc/specs/stdlib_hashmaps.md
jvdp1 Jun 17, 2022
dd120fe
Merge pull request #12 from jvdp1/hash_maps_review
wclodius2 Jun 18, 2022
fa71f36
add links in stdlib_hashmap_wrappers.f90
jvdp1 Jun 18, 2022
95bed1d
add links in stdlib_hashmaps
jvdp1 Jun 18, 2022
45b92c7
fix some typos in stdlib_hashmaps.md
jvdp1 Jun 18, 2022
45fd15c
Addition of test_maps using test-drive
jvdp1 Jun 18, 2022
f705430
Merge pull request #13 from jvdp1/hash_maps_review
wclodius2 Jun 19, 2022
a8cf88e
Merge pull request #14 from jvdp1/hash_maps_tests
wclodius2 Jun 19, 2022
bcf6aad
Merge branch 'master' of https://github.com/fortran-lang/stdlib into …
wclodius2 Jun 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions doc/specs/index.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ title: Specifications (specs)

# Fortran stdlib Specifications (specs)

This is and index/directory of the specifications (specs) for each new module/feature as described in the
This is an index/directory of the specifications (specs) for each new module/feature as described in the
[workflow document](../Workflow.html).

[TOC]
@@ -16,7 +16,8 @@ This is and index/directory of the specifications (specs) for each new module/fe
- [bitsets](./stdlib_bitsets.html) - Bitset data types and procedures
- [error](./stdlib_error.html) - Catching and handling errors
- [hash](./stdlib_hash_procedures.html) - Hashing integer
vectors or character strings
vectors or character strings
- [hashmaps](./stdlib_hashmaps.html) - Hash maps/tables
- [io](./stdlib_io.html) - Input/output helper & convenience
- [kinds](./stdlib_kinds.html) - Kind parameters
- [linalg](./stdlib_linalg.html) - Linear Algebra
2,127 changes: 2,127 additions & 0 deletions doc/specs/stdlib_hashmaps.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -84,6 +84,10 @@ fypp_f90("${fyppFlags}" "${fppFiles}" outFiles)
set(SRC
stdlib_array.f90
stdlib_error.f90
stdlib_hashmap_wrappers.f90
stdlib_hashmaps.f90
stdlib_hashmap_chaining.f90
stdlib_hashmap_open.f90
stdlib_logger.f90
stdlib_system.F90
stdlib_specialfunctions.f90
849 changes: 849 additions & 0 deletions src/stdlib_hashmap_chaining.f90

Large diffs are not rendered by default.

879 changes: 879 additions & 0 deletions src/stdlib_hashmap_open.f90

Large diffs are not rendered by default.

407 changes: 407 additions & 0 deletions src/stdlib_hashmap_wrappers.f90

Large diffs are not rendered by default.

811 changes: 811 additions & 0 deletions src/stdlib_hashmaps.f90

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ add_subdirectory(ascii)
add_subdirectory(bitsets)
add_subdirectory(hash_functions)
add_subdirectory(hash_functions_perf)
add_subdirectory(hashmaps)
add_subdirectory(io)
add_subdirectory(linalg)
add_subdirectory(logger)
13 changes: 13 additions & 0 deletions src/tests/hashmaps/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
### Pre-process: .fpp -> .f90 via Fypp

# Create a list of the files to be preprocessed
set(fppFiles
test_maps.fypp
)

fypp_f90("${fyppFlags}" "${fppFiles}" outFiles)

ADDTEST(chaining_maps)
ADDTEST(open_maps)
ADDTEST(maps)

5 changes: 5 additions & 0 deletions src/tests/hashmaps/Makefile.manual
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PROGS_SRC = test_chaining_maps.f90 \
test_open_maps.f90


include ../Makefile.manual.test.mk
294 changes: 294 additions & 0 deletions src/tests/hashmaps/test_chaining_maps.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
program test_chaining_maps
!! Test various aspects of the runtime system.
!! Running this program may require increasing the stack size to above 48 MBytes
!! or decreasing rand_power to 20 or less

use stdlib_kinds, only: &
dp, &
int8, &
int32

use stdlib_hashmaps, only : chaining_hashmap_type, int_depth, int_index
use stdlib_hashmap_wrappers

implicit none

type dummy_type
integer(int8), allocatable :: value(:)
end type dummy_type

integer(int32), parameter :: huge32 = huge(0_int32)
real(dp), parameter :: hugep1 = real(huge32, dp) + 1.0_dp
integer, parameter :: rand_power = 18
integer, parameter :: rand_size = 2**rand_power
integer, parameter :: test_size = rand_size*4
integer, parameter :: test_16 = 2**4
integer, parameter :: test_256 = 2**8

integer :: index
integer :: lun
type(chaining_hashmap_type) :: map
real(dp) :: rand2(2)
integer(int32) :: rand_object(rand_size)
integer(int8) :: test_8_bits(test_size)

open( newunit=lun, file="test_chaining_maps.txt", access="sequential", &
action="write", form="formatted", position="rewind" )
write(lun, '("| ", a17, " | ", a12, " | ", a15, " | ", a10, " |")') &
'Algorithm', 'Process', 'Data Set', 'Time (s)'

do index=1, rand_size
call random_number(rand2)
if (rand2(1) < 0.5_dp) then
rand_object(index) = ceiling(-rand2(2)*hugep1, int32) - 1
else
rand_object(index) = floor(rand2(2)*hugep1, int32)
end if
end do

test_8_bits(:) = transfer( rand_object, 0_int8, test_size )

call map % init( fnv_1_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'FNV-1', "16 byte words" )
call test_inquire_data( map, test_16, 'FNV-1', "16 byte words" )
call test_get_data( map, test_16, 'FNV-1', '16 byte words' )
call report_rehash_times( map, fnv_1_hasher, 'FNV-1', '16 byte words' )
call report_hash_statistics( map, 'FNV-1', '16 byte words' )
call report_removal_times( map, test_16, 'FNV-1', '16 byte words' )

call map % init( fnv_1_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'FNV-1', "256 byte words" )
call test_inquire_data( map, test_256, 'FNV-1', "256 byte words" )
call test_get_data( map, test_256, 'FNV-1', '256 byte words' )
call report_rehash_times( map, fnv_1_hasher, 'FNV-1', '256 byte words' )
call report_hash_statistics( map, 'FNV-1', '256 byte words' )
call report_removal_times( map, test_256, 'FNV-1', '256 byte words' )

call map % init( fnv_1a_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'FNV-1A', "16 byte words" )
call test_inquire_data( map, test_16, 'FNV-1A', "16 byte words" )
call test_get_data( map, test_16, 'FNV-1A', '16 byte words' )
call report_rehash_times( map, fnv_1a_hasher, 'FNV-1', '16 byte words' )
call report_hash_statistics( map, 'FNV-1A', '16 byte words' )
call report_removal_times( map, test_16, 'FNV-1a', '16 byte words' )

call map % init( fnv_1a_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'FNV-1A', "256 byte words" )
call test_inquire_data( map, test_256, 'FNV-1A', "256 byte words" )
call test_get_data( map, test_256, 'FNV-1A', '256 byte words' )
call report_rehash_times( map, fnv_1_hasher, 'FNV-1A', '256 byte words' )
call report_hash_statistics( map, 'FNV-1A', '256 byte words' )
call report_removal_times( map, test_256, 'FNV-1A', '256 byte words' )

call map % init( seeded_nmhash32_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'Seeded_Nmhash32', "16 byte words" )
call test_inquire_data( map, test_16, 'Seeded_Nmhash32', "16 byte words" )
call test_get_data( map, test_16, 'Seeded_Nmhash32', '16 byte words' )
call report_rehash_times( map, seeded_nmhash32_hasher, 'Seeded_Nmhash32', &
'16 byte words' )
call report_hash_statistics( map, 'Seeded_Nmhash32', '16 byte words' )
call report_removal_times( map, test_16, 'Seeded_Nmhash32', &
'16 byte words' )

call map % init( seeded_nmhash32_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'Seeded_Nmhash32', "256 byte words" )
call test_inquire_data( map, test_256, 'Seeded_Nmhash32', "256 byte words" )
call test_get_data( map, test_256, 'Seeded_Nmhash32', '256 byte words' )
call report_rehash_times( map, seeded_nmhash32_hasher, 'Seeded_Nmhash32', &
'256 byte words' )
call report_hash_statistics( map, 'Seeded_Nmhash32', '256 byte words' )
call report_removal_times( map, test_256, 'Seeded_Nmhash32', &
'256 byte words' )

call map % init( seeded_nmhash32x_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'Seeded_Nmhash32x', "16 byte words" )
call test_inquire_data( map, test_16, 'Seeded_Nmhash32x', "16 byte words" )
call test_get_data( map, test_16, 'Seeded_Nmhash32x', '16 byte words' )
call report_rehash_times( map, seeded_nmhash32x_hasher, &
'Seeded_Nmhash32x', '16 byte words' )
call report_hash_statistics( map, 'Seeded_Nmhash32x', '16 byte words' )
call report_removal_times( map, test_16, 'Seeded_Nmhash32x', &
'16 byte words' )

call map % init( seeded_nmhash32x_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'Seeded_Nmhash32x', &
"256 byte words" )
call test_inquire_data( map, test_256, 'Seeded_Nmhash32x', &
"256 byte words" )
call test_get_data( map, test_256, 'Seeded_Nmhash32x', '256 byte words' )
call report_rehash_times( map, seeded_nmhash32x_hasher, &
'Seeded_Nmhash32x', '256 byte words' )
call report_hash_statistics( map, 'Seeded_Nmhash32x', '256 byte words' )
call report_removal_times( map, test_256, 'Seeded_Nmhash32x', &
'256 byte words' )

call map % init( seeded_water_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'Seeded_Water', "16 byte words" )
call test_inquire_data( map, test_16, 'Seeded_Water', "16 byte words" )
call test_get_data( map, test_16, 'Seeded_Water', '16 byte words' )
call report_rehash_times( map, seeded_water_hasher, &
'Seeded_Water', '16 byte words' )
call report_hash_statistics( map, 'Seeded_Water', '16 byte words' )
call report_removal_times( map, test_16, 'Seeded_Water', &
'16 byte words' )

call map % init( seeded_water_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'Seeded_Water', &
"256 byte words" )
call test_inquire_data( map, test_256, 'Seeded_Water', &
"256 byte words" )
call test_get_data( map, test_256, 'Seeded_Water', '256 byte words' )
call report_rehash_times( map, seeded_water_hasher, &
'Seeded_Water', '256 byte words' )
call report_hash_statistics( map, 'Seeded_Water', '256 byte words' )
call report_removal_times( map, test_256, 'Seeded_Water', &
'256 byte words' )

contains

subroutine input_random_data( map, test_block, hash_name, size_name )
type(chaining_hashmap_type), intent(inout) :: map
integer(int_index), intent(in) :: test_block
character(*), intent(in) :: hash_name
character(*), intent(in) :: size_name
class(*), allocatable :: dummy
type(dummy_type) :: dummy_val
integer :: index2
type(key_type) :: key
type(other_type) :: other
real :: t1, t2, tdiff
logical :: conflict

call cpu_time(t1)
do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
if (allocated(dummy)) deallocate(dummy)
dummy_val % value = test_8_bits( index2:index2+test_block-1 )
allocate( dummy, source=dummy_val )
call set ( other, dummy )
call map % map_entry( key, other, conflict )
if (conflict) &
error stop "Unable to map entry because of a key conflict."
end do
call cpu_time(t2)
tdiff = t2-t1
write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Enter data', size_name, tdiff

end subroutine input_random_data


subroutine test_inquire_data( map, test_block, hash_name, size_name )
type(chaining_hashmap_type), intent(inout) :: map
integer(int_index), intent(in) :: test_block
character(*), intent(in) :: hash_name, size_name
integer :: index2
logical :: present
type(key_type) :: key
real :: t1, t2, tdiff

call cpu_time(t1)
do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % key_test( key, present )
if (.not. present) &
error stop "KEY not found in map KEY_TEST."
end do
call cpu_time(t2)
tdiff = t2-t1
write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Inquire data', size_name, tdiff

end subroutine test_inquire_data


subroutine test_get_data( map, test_block, hash_name, size_name )
type(chaining_hashmap_type), intent(inout) :: map
integer(int_index), intent(in) :: test_block
character(*), intent(in) :: hash_name, size_name
integer :: index2
type(key_type) :: key
type(other_type) :: other
logical :: exists
real :: t1, t2, tdiff

call cpu_time(t1)
do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % get_other_data( key, other, exists )
if (.not. exists) &
error stop "Unable to get data because key not found in map."
end do
call cpu_time(t2)
tdiff = t2-t1
write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Get data', size_name, tdiff

end subroutine test_get_data


subroutine report_rehash_times( map, hasher, hash_name, size_name )
type(chaining_hashmap_type), intent(inout) :: map
procedure(hasher_fun) :: hasher
character(*), intent(in) :: hash_name, size_name
real :: t1, t2, tdiff

call cpu_time(t1)
call map % rehash( hasher )
call cpu_time(t2)
tdiff = t2-t1

write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Rehash data', size_name, tdiff

end subroutine report_rehash_times


subroutine report_removal_times( map, test_block, hash_name, size_name )
type(chaining_hashmap_type), intent(inout) :: map
integer(int_index), intent(in) :: test_block
character(*), intent(in) :: hash_name, size_name
real :: t1, t2, tdiff
type(key_type) :: key
integer(int_index) :: index2
logical :: existed

call cpu_time(t1)
do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % remove(key, existed)
if ( .not. existed ) &
error stop "Key not found in entry removal."
end do
call cpu_time(t2)
tdiff = t2-t1

write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Remove data', size_name, tdiff
flush(lun)

end subroutine report_removal_times


subroutine report_hash_statistics( map, hash_name, size_name )
type(chaining_hashmap_type), intent(inout) :: map
character(*), intent(in) :: hash_name, size_name
integer(int_depth) :: depth

write(lun, *)
write(lun, '("Statistics for chaining hash table with ",' // &
'A, " hasher on ", A, ".")' ) hash_name, size_name
write(lun, '("Slots = ", I0)' ) map % num_slots()
write(lun, '("Calls = ", I0)' ) map % calls()
write(lun, '("Entries = ", I0)' ) map % entries()
write(lun, '("Total probes = ", I0)' ) map % map_probes()
write(lun, '("Loading = ", ES10.3)' ) map % loading()
depth = map % total_depth()
write(lun, '("Total depth = ", I0)' ) depth
write(lun, '("Relative depth = ", ES10.3)') &
real( depth ) / real( map % entries() )

end subroutine report_hash_statistics


end program test_chaining_maps
378 changes: 378 additions & 0 deletions src/tests/hashmaps/test_maps.fypp
Original file line number Diff line number Diff line change
@@ -0,0 +1,378 @@
#:set HASH_NAME = ["fnv_1_hasher", "fnv_1a_hasher", "seeded_nmhash32_hasher", "seeded_nmhash32x_hasher", "seeded_water_hasher"]
#:set SIZE_NAME = ["16", "256"]
module test_stdlib_chaining_maps
!! Test various aspects of the runtime system.
!! Running this program may require increasing the stack size to above 48 MBytes
!! or decreasing rand_power to 20 or less
use testdrive, only : new_unittest, unittest_type, error_type, check
use :: stdlib_kinds, only : dp, int8, int32
use stdlib_hashmaps, only : chaining_hashmap_type, int_depth, int_index
use stdlib_hashmap_wrappers

implicit none
private

type dummy_type
integer(int8), allocatable :: value(:)
end type dummy_type

integer(int32), parameter :: huge32 = huge(0_int32)
real(dp), parameter :: hugep1 = real(huge32, dp) + 1.0_dp
integer, parameter :: rand_power = 18
integer, parameter :: rand_size = 2**rand_power
integer, parameter :: test_size = rand_size*4
integer, parameter :: test_16 = 2**4
integer, parameter :: test_256 = 2**8

public :: collect_stdlib_chaining_maps

contains

!> Collect all exported unit tests
subroutine collect_stdlib_chaining_maps(testsuite)
!> Collection of tests
type(unittest_type), allocatable, intent(out) :: testsuite(:)

testsuite = [ &
new_unittest("chaining-maps-fnv_1_hasher-16-byte-words", test_fnv_1_hasher_16_byte_words) &
#:for hash_ in HASH_NAME
#:for size_ in SIZE_NAME
, new_unittest("chaining-maps-${hash_}$-${size_}$-byte-words", test_${hash_}$_${size_}$_byte_words) &
#:endfor
#:endfor
]

end subroutine collect_stdlib_chaining_maps

#:for hash_ in HASH_NAME
#:for size_ in SIZE_NAME
subroutine test_${hash_}$_${size_}$_byte_words(error)
!> Error handling
type(error_type), allocatable, intent(out) :: error

type(chaining_hashmap_type) :: map
integer(int8) :: test_8_bits(test_size)

call generate_vector(test_8_bits)

call map % init( ${hash_}$, slots_bits=10 )

call test_input_random_data(error, map, test_8_bits, test_${size_}$)
if (allocated(error)) return

call test_inquire_data(error, map, test_8_bits, test_${size_}$)
if (allocated(error)) return

call test_get_data(error, map, test_8_bits, test_${size_}$)
if (allocated(error)) return

call test_removal(error, map, test_8_bits, test_${size_}$)
if (allocated(error)) return

end subroutine
#:endfor
#:endfor


subroutine generate_vector(test_8_bits)
integer(int8), intent(out) :: test_8_bits(test_size)

integer :: index
real(dp) :: rand2(2)
integer(int32) :: rand_object(rand_size)

do index=1, rand_size
call random_number(rand2)
if (rand2(1) < 0.5_dp) then
rand_object(index) = ceiling(-rand2(2)*hugep1, int32) - 1
else
rand_object(index) = floor(rand2(2)*hugep1, int32)
end if
end do

test_8_bits(:) = transfer( rand_object, 0_int8, test_size )

end subroutine

subroutine test_input_random_data(error, map, test_8_bits, test_block)
type(error_type), allocatable, intent(out) :: error
type(chaining_hashmap_type), intent(inout) :: map
integer(int8), intent(in) :: test_8_bits(test_size)
integer(int_index), intent(in) :: test_block
class(*), allocatable :: dummy
type(dummy_type) :: dummy_val
integer :: index2
type(key_type) :: key
type(other_type) :: other
logical :: conflict

do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
if (allocated(dummy)) deallocate(dummy)
dummy_val % value = test_8_bits( index2:index2+test_block-1 )
allocate( dummy, source=dummy_val )
call set ( other, dummy )
call map % map_entry( key, other, conflict )
call check(error, .not.conflict, "Unable to map entry because of a key conflict.")
if (allocated(error)) return
end do

end subroutine

subroutine test_inquire_data(error, map, test_8_bits, test_block)
type(error_type), allocatable, intent(out) :: error
type(chaining_hashmap_type), intent(inout) :: map
integer(int8), intent(in) :: test_8_bits(test_size)
integer(int_index), intent(in) :: test_block
integer :: index2
logical :: present
type(key_type) :: key

do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % key_test( key, present )
call check(error, present, "KEY not found in map KEY_TEST.")
if (allocated(error)) return
end do

end subroutine

subroutine test_get_data(error, map, test_8_bits, test_block)
type(error_type), allocatable, intent(out) :: error
type(chaining_hashmap_type), intent(inout) :: map
integer(int8), intent(in) :: test_8_bits(test_size)
integer(int_index), intent(in) :: test_block
integer :: index2
type(key_type) :: key
type(other_type) :: other
logical :: exists

do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % get_other_data( key, other, exists )
call check(error, exists, "Unable to get data because key not found in map.")
end do

end subroutine

subroutine test_removal(error, map, test_8_bits, test_block)
type(error_type), allocatable, intent(out) :: error
type(chaining_hashmap_type), intent(inout) :: map
integer(int8), intent(in) :: test_8_bits(test_size)
integer(int_index), intent(in) :: test_block
type(key_type) :: key
integer(int_index) :: index2
logical :: existed

do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % remove(key, existed)
call check(error, existed, "Key not found in entry removal.")
end do

end subroutine

end module

module test_stdlib_open_maps
!! Test various aspects of the runtime system.
!! Running this program may require increasing the stack size to above 48 MBytes
!! or decreasing rand_power to 20 or less
use testdrive, only : new_unittest, unittest_type, error_type, check
use :: stdlib_kinds, only : dp, int8, int32
use stdlib_hashmaps, only : open_hashmap_type, int_depth, int_index
use stdlib_hashmap_wrappers

implicit none
private

type dummy_type
integer(int8), allocatable :: value(:)
end type dummy_type

integer(int32), parameter :: huge32 = huge(0_int32)
real(dp), parameter :: hugep1 = real(huge32, dp) + 1.0_dp
integer, parameter :: rand_power = 18
integer, parameter :: rand_size = 2**rand_power
integer, parameter :: test_size = rand_size*4
integer, parameter :: test_16 = 2**4
integer, parameter :: test_256 = 2**8

public :: collect_stdlib_open_maps

contains

!> Collect all exported unit tests
subroutine collect_stdlib_open_maps(testsuite)
!> Collection of tests
type(unittest_type), allocatable, intent(out) :: testsuite(:)

testsuite = [ &
new_unittest("open-maps-fnv_1_hasher-16-byte-words", test_fnv_1_hasher_16_byte_words) &
#:for hash_ in HASH_NAME
#:for size_ in SIZE_NAME
, new_unittest("open-maps-${hash_}$-${size_}$-byte-words", test_${hash_}$_${size_}$_byte_words) &
#:endfor
#:endfor
]

end subroutine collect_stdlib_open_maps

#:for hash_ in HASH_NAME
#:for size_ in SIZE_NAME
subroutine test_${hash_}$_${size_}$_byte_words(error)
!> Error handling
type(error_type), allocatable, intent(out) :: error

type(open_hashmap_type) :: map
integer(int8) :: test_8_bits(test_size)

call generate_vector(test_8_bits)

call map % init( ${hash_}$, slots_bits=10 )

call test_input_random_data(error, map, test_8_bits, test_${size_}$)
if (allocated(error)) return

call test_inquire_data(error, map, test_8_bits, test_${size_}$)
if (allocated(error)) return

call test_get_data(error, map, test_8_bits, test_${size_}$)
if (allocated(error)) return

call test_removal(error, map, test_8_bits, test_${size_}$)
if (allocated(error)) return

end subroutine
#:endfor
#:endfor


subroutine generate_vector(test_8_bits)
integer(int8), intent(out) :: test_8_bits(test_size)

integer :: index
real(dp) :: rand2(2)
integer(int32) :: rand_object(rand_size)

do index=1, rand_size
call random_number(rand2)
if (rand2(1) < 0.5_dp) then
rand_object(index) = ceiling(-rand2(2)*hugep1, int32) - 1
else
rand_object(index) = floor(rand2(2)*hugep1, int32)
end if
end do

test_8_bits(:) = transfer( rand_object, 0_int8, test_size )

end subroutine

subroutine test_input_random_data(error, map, test_8_bits, test_block)
type(error_type), allocatable, intent(out) :: error
type(open_hashmap_type), intent(inout) :: map
integer(int8), intent(in) :: test_8_bits(test_size)
integer(int_index), intent(in) :: test_block
class(*), allocatable :: dummy
type(dummy_type) :: dummy_val
integer :: index2
type(key_type) :: key
type(other_type) :: other
logical :: conflict

do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
if (allocated(dummy)) deallocate(dummy)
dummy_val % value = test_8_bits( index2:index2+test_block-1 )
allocate( dummy, source=dummy_val )
call set ( other, dummy )
call map % map_entry( key, other, conflict )
call check(error, .not.conflict, "Unable to map entry because of a key conflict.")
if (allocated(error)) return
end do

end subroutine

subroutine test_inquire_data(error, map, test_8_bits, test_block)
type(error_type), allocatable, intent(out) :: error
type(open_hashmap_type), intent(inout) :: map
integer(int8), intent(in) :: test_8_bits(test_size)
integer(int_index), intent(in) :: test_block
integer :: index2
logical :: present
type(key_type) :: key

do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % key_test( key, present )
call check(error, present, "KEY not found in map KEY_TEST.")
if (allocated(error)) return
end do

end subroutine

subroutine test_get_data(error, map, test_8_bits, test_block)
type(error_type), allocatable, intent(out) :: error
type(open_hashmap_type), intent(inout) :: map
integer(int8), intent(in) :: test_8_bits(test_size)
integer(int_index), intent(in) :: test_block
integer :: index2
type(key_type) :: key
type(other_type) :: other
logical :: exists

do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % get_other_data( key, other, exists )
call check(error, exists, "Unable to get data because key not found in map.")
end do

end subroutine

subroutine test_removal(error, map, test_8_bits, test_block)
type(error_type), allocatable, intent(out) :: error
type(open_hashmap_type), intent(inout) :: map
integer(int8), intent(in) :: test_8_bits(test_size)
integer(int_index), intent(in) :: test_block
type(key_type) :: key
integer(int_index) :: index2
logical :: existed

do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % remove(key, existed)
call check(error, existed, "Key not found in entry removal.")
end do

end subroutine

end module


program tester
use, intrinsic :: iso_fortran_env, only : error_unit
use testdrive, only : run_testsuite, new_testsuite, testsuite_type
use test_stdlib_open_maps, only : collect_stdlib_open_maps
use test_stdlib_chaining_maps, only : collect_stdlib_chaining_maps
implicit none
integer :: stat, is
type(testsuite_type), allocatable :: testsuites(:)
character(len=*), parameter :: fmt = '("#", *(1x, a))'

stat = 0

testsuites = [ &
new_testsuite("stdlib-open-maps", collect_stdlib_open_maps) &
, new_testsuite("stdlib-chaining-maps", collect_stdlib_chaining_maps) &
]

do is = 1, size(testsuites)
write(error_unit, fmt) "Testing:", testsuites(is)%name
call run_testsuite(testsuites(is)%collect, error_unit, stat)
end do

if (stat > 0) then
write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!"
error stop
end if
end program
295 changes: 295 additions & 0 deletions src/tests/hashmaps/test_open_maps.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
program test_open_maps
!! Test various aspects of the runtime system.
!! Running this program may require increasing the stack size to above 48 MBytes
!! or decreasing rand_power to 20 or less

use stdlib_kinds, only: &
dp, &
int8, &
int32

use stdlib_hashmaps, only : open_hashmap_type, int_depth, int_index
use stdlib_hashmap_wrappers

implicit none

type dummy_type
integer(int8), allocatable :: value(:)
end type dummy_type

integer(int32), parameter :: huge32 = huge(0_int32)
real(dp), parameter :: hugep1 = real(huge32, dp) + 1.0_dp
integer, parameter :: rand_power = 18
integer, parameter :: rand_size = 2**rand_power
integer, parameter :: test_size = rand_size*4
integer, parameter :: test_16 = 2**4
integer, parameter :: test_256 = 2**8

integer :: index
integer :: lun
type(open_hashmap_type) :: map
real(dp) :: rand2(2)
integer(int32) :: rand_object(rand_size)
integer(int8) :: test_8_bits(test_size)


open( newunit=lun, file="test_open_maps.txt", access="sequential", &
action="write", form="formatted", position="rewind" )
write(lun, '("| ", a17, " | ", a12, " | ", a15, " | ", a10, " |")') &
'Algorithm', 'Process', 'Data Set', 'Time (s)'

do index=1, rand_size
call random_number(rand2)
if (rand2(1) < 0.5_dp) then
rand_object(index) = ceiling(-rand2(2)*hugep1, int32) - 1
else
rand_object(index) = floor(rand2(2)*hugep1, int32)
end if
end do

test_8_bits(:) = transfer( rand_object, 0_int8, test_size )

call map % init( fnv_1_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'FNV-1', "16 byte words" )
call test_inquire_data( map, test_16, 'FNV-1', "16 byte words" )
call test_get_data( map, test_16, 'FNV-1', '16 byte words' )
call report_rehash_times( map, fnv_1_hasher, 'FNV-1', '16 byte words' )
call report_hash_statistics( map, 'FNV-1', '16 byte words' )
call report_removal_times( map, test_16, 'FNV-1', '16 byte words' )

call map % init( fnv_1_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'FNV-1', "256 byte words" )
call test_inquire_data( map, test_256, 'FNV-1', "256 byte words" )
call test_get_data( map, test_256, 'FNV-1', '256 byte words' )
call report_rehash_times( map, fnv_1_hasher, 'FNV-1', '256 byte words' )
call report_hash_statistics( map, 'FNV-1', '256 byte words' )
call report_removal_times( map, test_256, 'FNV-1', '256 byte words' )

call map % init( fnv_1a_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'FNV-1A', "16 byte words" )
call test_inquire_data( map, test_16, 'FNV-1A', "16 byte words" )
call test_get_data( map, test_16, 'FNV-1A', '16 byte words' )
call report_rehash_times( map, fnv_1a_hasher, 'FNV-1', '16 byte words' )
call report_hash_statistics( map, 'FNV-1A', '16 byte words' )
call report_removal_times( map, test_16, 'FNV-1a', '16 byte words' )

call map % init( fnv_1a_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'FNV-1A', "256 byte words" )
call test_inquire_data( map, test_256, 'FNV-1A', "256 byte words" )
call test_get_data( map, test_256, 'FNV-1A', '256 byte words' )
call report_rehash_times( map, fnv_1_hasher, 'FNV-1A', '256 byte words' )
call report_hash_statistics( map, 'FNV-1A', '256 byte words' )
call report_removal_times( map, test_256, 'FNV-1A', '256 byte words' )

call map % init( seeded_nmhash32_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'Seeded_Nmhash32', "16 byte words" )
call test_inquire_data( map, test_16, 'Seeded_Nmhash32', "16 byte words" )
call test_get_data( map, test_16, 'Seeded_Nmhash32', '16 byte words' )
call report_rehash_times( map, seeded_nmhash32_hasher, 'Seeded_Nmhash32', &
'16 byte words' )
call report_hash_statistics( map, 'Seeded_Nmhash32', '16 byte words' )
call report_removal_times( map, test_16, 'Seeded_Nmhash32', &
'16 byte words' )

call map % init( seeded_nmhash32_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'Seeded_Nmhash32', "256 byte words" )
call test_inquire_data( map, test_256, 'Seeded_Nmhash32', "256 byte words" )
call test_get_data( map, test_256, 'Seeded_Nmhash32', '256 byte words' )
call report_rehash_times( map, seeded_nmhash32_hasher, 'Seeded_Nmhash32', &
'256 byte words' )
call report_hash_statistics( map, 'Seeded_Nmhash32', '256 byte words' )
call report_removal_times( map, test_256, 'Seeded_Nmhash32', &
'256 byte words' )

call map % init( seeded_nmhash32x_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'Seeded_Nmhash32x', "16 byte words" )
call test_inquire_data( map, test_16, 'Seeded_Nmhash32x', "16 byte words" )
call test_get_data( map, test_16, 'Seeded_Nmhash32x', '16 byte words' )
call report_rehash_times( map, seeded_nmhash32x_hasher, &
'Seeded_Nmhash32x', '16 byte words' )
call report_hash_statistics( map, 'Seeded_Nmhash32x', '16 byte words' )
call report_removal_times( map, test_16, 'Seeded_Nmhash32x', &
'16 byte words' )

call map % init( seeded_nmhash32x_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'Seeded_Nmhash32x', &
"256 byte words" )
call test_inquire_data( map, test_256, 'Seeded_Nmhash32x', &
"256 byte words" )
call test_get_data( map, test_256, 'Seeded_Nmhash32x', '256 byte words' )
call report_rehash_times( map, seeded_nmhash32x_hasher, &
'Seeded_Nmhash32x', '256 byte words' )
call report_hash_statistics( map, 'Seeded_Nmhash32x', '256 byte words' )
call report_removal_times( map, test_256, 'Seeded_Nmhash32x', &
'256 byte words' )

call map % init( seeded_water_hasher, slots_bits=10 )
call input_random_data( map, test_16, 'Seeded_Water', "16 byte words" )
call test_inquire_data( map, test_16, 'Seeded_Water', "16 byte words" )
call test_get_data( map, test_16, 'Seeded_Water', '16 byte words' )
call report_rehash_times( map, seeded_water_hasher, &
'Seeded_Water', '16 byte words' )
call report_hash_statistics( map, 'Seeded_Water', '16 byte words' )
call report_removal_times( map, test_16, 'Seeded_Water', &
'16 byte words' )

call map % init( seeded_water_hasher, slots_bits=10 )
call input_random_data( map, test_256, 'Seeded_Water', &
"256 byte words" )
call test_inquire_data( map, test_256, 'Seeded_Water', &
"256 byte words" )
call test_get_data( map, test_256, 'Seeded_Water', '256 byte words' )
call report_rehash_times( map, seeded_water_hasher, &
'Seeded_Water', '256 byte words' )
call report_hash_statistics( map, 'Seeded_Water', '256 byte words' )
call report_removal_times( map, test_256, 'Seeded_Water', &
'256 byte words' )

contains

subroutine input_random_data( map, test_block, hash_name, size_name )
type(open_hashmap_type), intent(inout) :: map
integer(int_index), intent(in) :: test_block
character(*), intent(in) :: hash_name
character(*), intent(in) :: size_name
class(*), allocatable :: dummy
type(dummy_type) :: dummy_val
integer :: index2
type(key_type) :: key
type(other_type) :: other
real :: t1, t2, tdiff
logical :: conflict

call cpu_time(t1)
do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
if (allocated(dummy)) deallocate(dummy)
dummy_val % value = test_8_bits( index2:index2+test_block-1 )
allocate( dummy, source=dummy_val )
call set ( other, dummy )
call map % map_entry( key, other, conflict )
if (conflict) &
error stop "Unable to map entry because of a key conflict."
end do
call cpu_time(t2)
tdiff = t2-t1
write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Enter data', size_name, tdiff

end subroutine input_random_data


subroutine test_inquire_data( map, test_block, hash_name, size_name )
type(open_hashmap_type), intent(inout) :: map
integer(int_index), intent(in) :: test_block
character(*), intent(in) :: hash_name, size_name
integer :: index2
logical :: present
type(key_type) :: key
real :: t1, t2, tdiff

call cpu_time(t1)
do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % key_test( key, present )
if (.not. present) &
error stop "KEY not found in map KEY_TEST."
end do
call cpu_time(t2)
tdiff = t2-t1
write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Inquire data', size_name, tdiff

end subroutine test_inquire_data


subroutine test_get_data( map, test_block, hash_name, size_name )
type(open_hashmap_type), intent(inout) :: map
integer(int_index), intent(in) :: test_block
character(*), intent(in) :: hash_name, size_name
integer :: index2
type(key_type) :: key
type(other_type) :: other
logical :: exists
real :: t1, t2, tdiff

call cpu_time(t1)
do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % get_other_data( key, other, exists )
if (.not. exists) &
error stop "Unable to get data because key not found in map."
end do
call cpu_time(t2)
tdiff = t2-t1
write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Get data', size_name, tdiff

end subroutine test_get_data


subroutine report_rehash_times( map, hasher, hash_name, size_name )
type(open_hashmap_type), intent(inout) :: map
procedure(hasher_fun) :: hasher
character(*), intent(in) :: hash_name, size_name
real :: t1, t2, tdiff

call cpu_time(t1)
call map % rehash( hasher )
call cpu_time(t2)
tdiff = t2-t1

write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Rehash data', size_name, tdiff

end subroutine report_rehash_times


subroutine report_removal_times( map, test_block, hash_name, size_name )
type(open_hashmap_type), intent(inout) :: map
integer(int_index), intent(in) :: test_block
character(*), intent(in) :: hash_name, size_name
real :: t1, t2, tdiff
type(key_type) :: key
integer(int_index) :: index2
logical :: existed

call cpu_time(t1)
do index2=1, size(test_8_bits), test_block
call set( key, test_8_bits( index2:index2+test_block-1 ) )
call map % remove(key, existed)
if ( .not. existed ) &
error stop "Key not found in entry removal."
end do
call cpu_time(t2)
tdiff = t2-t1

write(lun, '("|", a18, " | ", a12, " | ", a15, " | ", f10.5, " |")') &
trim(hash_name), 'Remove data', size_name, tdiff
flush(lun)

end subroutine report_removal_times


subroutine report_hash_statistics( map, hash_name, size_name )
type(open_hashmap_type), intent(inout) :: map
character(*), intent(in) :: hash_name, size_name
integer(int_depth) :: depth

write(lun, *)
write(lun, '("Statistics for open hash table with ",' // &
'A, " hasher on ", A, ".")' ) hash_name, size_name
write(lun, '("Slots = ", I0)' ) map % num_slots()
write(lun, '("Calls = ", I0)' ) map % calls()
write(lun, '("Entries = ", I0)' ) map % entries()
write(lun, '("Total probes = ", I0)' ) map % map_probes()
write(lun, '("Loading = ", ES10.3)' ) map % loading()
depth = map % total_depth()
write(lun, '("Total depth = ", I0)' ) depth
write(lun, '("Relative depth = ", ES10.3)') &
real( depth ) / real( map % entries() )

end subroutine report_hash_statistics


end program test_open_maps