stdlib_hash_32bit_nm.fypp 源文件

NM_HASH32NM_HASH32X 是对 James Z. M. Gao 于 2021 年版权的 nmhash32nmhash32x 标量算法的 Fortran 2008 和有符号二进制补码算术的翻译。James Z. M. Gao 的原始 C++ 代码 nmhash.h 可在以下 URL 获取:https://github.com/gzm55/hash-garage/blob/a8913138bdb3b7539c202edee30a7f0794bbd835/nmhash.h,根据 BSD 2 条款许可证:https://github.com/gzm55/hash-garage/blob/a8913138bdb3b7539c202edee30a7f0794bbd835/LICENSE 这些算法有多个版本,具体取决于是否可以使用矢量化指令 SSE2 或 AVX2。由于这两种指令在可移植的 Fortran 2008 中都不可用,因此使用了不使用这些指令的算法。

BSD 2 条款许可证如下所示

BSD 2 条款许可证

版权所有 (c) 2021,水哈希算法。James Z.M. Gao 保留所有权利。

在满足以下条件的情况下,允许以源代码和二进制形式重新分发和使用,无论是否修改。

  1. 源代码的重新分发必须保留上述版权声明、此条件列表以及以下免责声明。

  2. 二进制形式的重新分发必须在随分发提供的文档和/或其他材料中复制上述版权声明、此条件列表以及以下免责声明。

本软件由版权持有人和贡献者“按原样”提供,并且不提供任何明示或暗示的担保,包括但不限于对适销性和特定用途适用性的暗示担保。在任何情况下,版权持有人或贡献者均不对任何直接、间接、偶然、特殊、惩罚性或后果性损害(包括但不限于替代商品或服务的采购;使用、数据或利润损失;或业务中断)负责,无论这些损害是如何造成的以及基于何种责任理论,无论是基于合同、严格责任还是侵权行为(包括疏忽或其他),即使已被告知此类损害的可能性。




源代码

!!------------------------------------------------------------------------------
!! `NM_HASH32` and `NM_HASH32X` are translations to Fortran 2008 and signed
!! two's complement arithmetic of the `nmhash32` and `nmhash32x` scalar
!! algorithms of James Z. M. Gao, copyright 2021. James Z. M. Gao's original
!! C++ code, `nmhash.h`, is available at the URL:
!! https://github.com/gzm55/hash-garage/blob/a8913138bdb3b7539c202edee30a7f0794bbd835/nmhash.h
!! under the BSD 2-Clause License:
!! https://github.com/gzm55/hash-garage/blob/a8913138bdb3b7539c202edee30a7f0794bbd835/LICENSE
!! The algorithms come in multiple versions, depending on whether the
!! vectorized instructions SSE2 or AVX2 are available. As neither instruction
!! is available in portable Fortran 2008, the algorithms that do not use these
!! instructions are used.
!!
!! The BSD 2-Clause license is as follows:
!!
!! BSD 2-Clause License
!!
!! Copyright (c) 2021, water hash algorithm. James Z.M. Gao
!! All rights reserved.
!!
!! Redistribution and use in source and binary forms, with or without
!! modification, are permitted provided that the following conditions are met:
!!
!! 1. Redistributions of source code must retain the above copyright notice,
!!    this list of conditions and the following disclaimer.
!!
!! 2. Redistributions in binary form must reproduce the above copyright notice,
!!    this list of conditions and the following disclaimer in the documentation
!!    and/or other materials provided with the distribution.
!!
!! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
!! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
!! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
!! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
!! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
!! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
!! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
!! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
!! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
!! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
!! POSSIBILITY OF SUCH DAMAGE.
!!------------------------------------------------------------------------------

#! Integer kinds to be considered during templating
#:set INT_KINDS = ["int16", "int32", "int64"]

submodule(stdlib_hash_32bit) stdlib_hash_32bit_nm

    implicit none

! Primes from XXH
    integer(int32), parameter :: nmh_prime32_1 = int( Z'9E3779B1', int32 )
    integer(int32), parameter :: nmh_prime32_2 = int( Z'85EBCA77', int32 )
    integer(int32), parameter :: nmh_prime32_3 = int( Z'C2B2AE3D', int32 )
    integer(int32), parameter :: nmh_prime32_4 = int( Z'27D4EB2F', int32 )

    integer(int32), parameter :: nmh_m1 = int(z'F0D9649B', int32 )
    integer(int32), parameter :: nmh_m2 = int(z'29A7935D', int32 )
    integer(int32), parameter :: nmh_m3 = int(z'55D35831', int32 )

    integer(int32), parameter :: nmh_m1_v(0:31) = nmh_m1
    integer(int32), parameter :: nmh_m2_v(0:31) = nmh_m2
    integer(int32), parameter :: nmh_m3_v(0:31) = nmh_m3

    logical, parameter :: nmh_short32_without_seed2=.false.
    logical, parameter :: nmh_short32_with_seed2=.true.

    integer, parameter :: init_size = 32

! Pseudorandom secrets taken directly from FARSH.
    integer(int32), parameter :: nmh_acc_init(0:init_size-1) = [ &
        int( z'B8FE6C39', int32 ), int( z'23A44BBE', int32 ), &
        int( z'7C01812C', int32 ), int( z'F721AD1C', int32 ), &
        int( z'DED46DE9', int32 ), int( z'839097DB', int32 ), &
        int( z'7240A4A4', int32 ), int( z'B7B3671F', int32 ), &
        int( z'CB79E64E', int32 ), int( z'CCC0E578', int32 ), &
        int( z'825AD07D', int32 ), int( z'CCFF7221', int32 ), &
        int( z'B8084674', int32 ), int( z'F743248E', int32 ), &
        int( z'E03590E6', int32 ), int( z'813A264C', int32 ), &

        int( z'3C2852BB', int32 ), int( z'91C300CB', int32 ), &
        int( z'88D0658B', int32 ), int( z'1B532EA3', int32 ), &
        int( z'71644897', int32 ), int( z'A20DF94E', int32 ), &
        int( z'3819EF46', int32 ), int( z'A9DEACD8', int32 ), &
        int( z'A8FA763F', int32 ), int( z'E39C343F', int32 ), &
        int( z'F9DCBBC7', int32 ), int( z'C70B4F1D', int32 ), &
        int( z'8A51E04B', int32 ), int( z'CDB45931', int32 ), &
        int( z'C89F7EC9', int32 ), int( z'D9787364', int32 ) ]

contains

    pure function nmh_readle32( p ) result( v )
        integer(int32) :: v
        integer(int8), intent(in) :: p(:)

        if ( little_endian ) then
            v = transfer( p(1:4), 0_int32 )
        else
            v = transfer( [ p(4), p(3), p(2), p(1) ], 0_int32 )
        end if

    end function nmh_readle32

    pure function nmh_readle16( p ) result( v )
        integer(int16) :: v
        integer(int8), intent(in) :: p(:)

        if ( little_endian ) then
            v = transfer( p(1:2), 0_int16 )
        else
            v = transfer( [ p(2), p(1) ], 0_int16 )
        end if

    end function nmh_readle16

    pure function nmhash32_0to8( x, seed ) result( vx32 )
        integer(int32), intent(in) :: x
        integer(int32), intent(in) :: seed
        integer(int32) :: vx32
        ! base mixer: [-6 -12 776bf593 -19 11 3fb39c65 -15 -9 e9139917 -11 16]
        ! = 0.027071104091278835
        integer(int32), parameter :: m1 = int(z'776BF593', int32)
        integer(int32), parameter :: m2 = int(z'3FB39C65', int32)
        integer(int32), parameter :: m3 = int(z'E9139917', int32)

        integer(int16) :: vx16(2)

        vx32 = x
        vx32 = ieor( vx32, ieor( ishft( vx32, -12 ), ishft( vx32, -6 ) ) )
        vx16 = transfer( vx32, 0_int16, 2 )
        vx16 = vx16 * transfer( m1, 0_int16, 2 )
        vx32 = transfer( vx16, 0_int32 )
        vx32 = ieor( vx32, ieor( ishft( vx32, 11 ), ishft( vx32, -19 ) ) )
        vx16 = transfer( vx32, 0_int16, 2 )
        vx16 = vx16 * transfer( m2, 0_int16, 2 )
        vx32 = transfer( vx16, 0_int32 )
        vx32 = ieor( vx32, seed )
        vx32 = ieor( vx32, ieor( ishft( vx32, -15 ), ishft( vx32, -9 ) ) )
        vx16 = transfer( vx32, 0_int16, 2 )
        vx16 = vx16 * transfer( m3, 0_int16, 2 )
        vx32 = transfer( vx16, 0_int32 )
        vx32 = ieor( vx32, ieor( ishft(vx32, 16), ishft(vx32, -11) ) )

    end function nmhash32_0to8

    pure function nmhash32_9to255( p, seed, full_avalanche ) result( result )
        integer(int8), intent(in)  :: p(0:)
        integer(int32), intent(in) :: seed
        logical, intent(in)        :: full_avalanche
        integer(int32) :: result

        integer(int32) :: xu32(0:3), yu32(0:3)
        integer(int16) :: xu16(0:1)
!       Due to an issue with Intel OneAPI ifort 2021 (see  
!       https://community.intel.com/t5/Intel-Fortran-Compiler/Intrinsic-transfer-with-a-provided-size-un-expected-behavior/m-p/1343313#M158733
!       ), it is not possible to define the following variables as a parameter.
!        integer(int16), parameter :: &
!            nmh_m1_16(0:1) = transfer( nmh_m1, 0_int16, 2 ),  &
!            nmh_m2_16(0:1) = transfer( nmh_m2, 0_int16, 2 ),  &
!            nmh_m3_16(0:1) = transfer( nmh_m3, 0_int16, 2 )
        integer(int16) :: nmh_m1_16(0:1), nmh_m2_16(0:1), nmh_m3_16(0:1)
        integer(int32) :: s1
        integer(int64) :: length
        integer(int32) :: length32(0:1)
        integer(int64) :: i, j, r

        nmh_m1_16(0:1) = transfer( nmh_m1, 0_int16, 2 )
        nmh_m2_16(0:1) = transfer( nmh_m2, 0_int16, 2 )
        nmh_m3_16(0:1) = transfer( nmh_m3, 0_int16, 2 )

        ! base mixer: [f0d9649b  5 -13 29a7935d -9 11 55d35831 -20 -10 ] =
        ! 0.93495901789135362

        result = 0
        length = size( p, kind=int64 )
        length32 = transfer(length, 0_int32, 2)
        if (little_endian) then
            s1 = seed + length32(0)
        else
            s1 = seed + length32(1)
        end if
        xu32(0) = nmh_prime32_1
        xu32(1) = nmh_prime32_2
        xu32(2) = nmh_prime32_3
        xu32(3) = nmh_prime32_4
        yu32(:) = s1

        if (full_avalanche) then
            ! 33 to 255 bytes
            r = (length - 1 ) /32
            do i=0, r-1
                do j=0, 3
                    xu32(j) = ieor( xu32(j), nmh_readle32( p(i*32 + j*4: ) ) )
                    yu32(j) = ieor( yu32(j), &
                                    nmh_readle32( p(i*32 + j*4 + 16: ) ) )
                    xu32(j) = xu32(j) + yu32(j)
                    xu16 = transfer( xu32(j), 0_int16, 2 )
                    xu16 = xu16 * nmh_m1_16
                    xu32(j) = transfer( xu16, 0_int32 )
                    xu32(j) = ieor( xu32(j), &
                                    ieor( ishft(xu32(j), 5), &
                                          ishft(xu32(j), -13)) )
                    xu16 = transfer( xu32(j), 0_int16, 2 )
                    xu16 = xu16 * nmh_m2_16
                    xu32(j) = transfer( xu16, 0_int32 )
                    xu32(j) = ieor( xu32(j), yu32(j) )
                    xu32(j) = ieor( xu32(j), &
                                    ieor( ishft(xu32(j), 11), &
                                          ishft(xu32(j), -9) ) )
                    xu16 = transfer( xu32(j), 0_int16, 2 )
                    xu16 = xu16 * nmh_m3_16
                    xu32(j) = transfer( xu16, 0_int32 )
                    xu32(j) = ieor( xu32(j), &
                                    ieor( ishft(xu32(j),-10), &
                                          ishft(xu32(j), -20) ) )
                end do
            end do
            do j=0, 3
                xu32(j) = ieor( xu32(j), &
                                nmh_readle32( p(length - 32 + j*4: ) ) )
                yu32(j) = ieor( yu32(j), &
                                nmh_readle32( p(length - 16 + j*4: ) ) )
            end do
        else
            ! 9 to 32 bytes
            xu32(0) = ieor(xu32(0), nmh_readle32(p(0:)))
            xu32(1) = ieor(xu32(1), nmh_readle32(p(ishft(ishft(length,-4),3):)))
            xu32(2) = ieor(xu32(2), nmh_readle32(p(length-8:)))
            xu32(3) = ieor(xu32(3), &
                           nmh_readle32(p(length-8-ishft(ishft(length,-4),3):)))
            yu32(0) = ieor(yu32(0), nmh_readle32(p(4:)))
            yu32(1) = ieor(yu32(1), &
                      nmh_readle32(p(ishft(ishft(length,-4),3)+4:)))
            yu32(2) = ieor(yu32(2), nmh_readle32(p(length-8+4:)))
            yu32(3) = ieor(yu32(3), &
                           nmh_readle32(p(length - 8 - &
                                        ishft(ishft(length,-4),3)+4:)))
        end if
        do j=0, 3
            xu32(j) = xu32(j) + yu32(j)
            yu32(j) = ieor( yu32(j), ieor(ishft(yu32(j), 17), &
                                          ishft(yu32(j), -6) ) )
            xu16 = transfer( xu32(j), 0_int16, 2 )
            xu16 = xu16 * nmh_m1_16
            xu32(j) = transfer( xu16, 0_int32 )
            xu32(j) = ieor( xu32(j), ieor(ishft(xu32(j), 5), &
                                          ishft(xu32(j), -13) ) )
            xu16 = transfer( xu32(j), 0_int16, 2 )
            xu16 = xu16 * nmh_m2_16
            xu32(j) = transfer( xu16, 0_int32 )
            xu32(j) = ieor( xu32(j), yu32(j) )
            xu32(j) = ieor( xu32(j), ieor(ishft(xu32(j), 11), &
                                          ishft(xu32(j), -9) ) )
            xu16 = transfer( xu32(j), 0_int16, 2 )
            xu16 = xu16 * nmh_m3_16
            xu32(j) = transfer( xu16, 0_int32 )
            xu32(j) = ieor( xu32(j), ieor(ishft(xu32(j), -10), &
                                          ishft(xu32(j), -20) ) )
        end do
        xu32(0) = ieor( xu32(0), nmh_prime32_1 )
        xu32(1) = ieor( xu32(1), nmh_prime32_2 )
        xu32(2) = ieor( xu32(2), nmh_prime32_3 )
        xu32(3) = ieor( xu32(3), nmh_prime32_4 )
        do j=1, 3
            xu32(0) = xu32(0) + xu32(j)
        end do
        xu32(0) = ieor(xu32(0), s1 + ishft(s1, -5) )
        xu16 = transfer( xu32(0), 0_int16, 2 )
        xu16 = xu16 * nmh_m3_16
        xu32(0) = transfer( xu16, 0_int32 )
        xu32(0) = ieor(xu32(0), &
                       ieor(ishft(xu32(0), -10), ishft(xu32(0), -20) ) )
        result = xu32(0)

    end function nmhash32_9to255

    pure function nmhash32_9to32( p, seed ) result( result )
        integer(int8), intent(in)  :: p(0:)
        integer(int32), intent(in) :: seed
        integer(int32) :: result

        result = nmhash32_9to255( p, seed, .false. )

    end function nmhash32_9to32

    pure function nmhash32_33to255( p, seed ) result( result )
        integer(int8), intent(in)  :: p(0:)
        integer(int32), intent(in) :: seed
        integer(int32) :: result

        result = nmhash32_9to255( p, seed, .true. )

    end function nmhash32_33to255

    pure subroutine nmhash32_long_round( accx, accy, p )
        integer(int32), intent(inout) :: accx(0:)
        integer(int32), intent(inout) :: accy(0:)
        integer(int8), intent(in)     :: p(0:)

        integer(int64), parameter :: nbgroups = init_size
        integer(int64) :: i
        integer(int16) :: dummy1(0:1)
        integer(int16) :: dummy2(0:1)

        do i = 0, nbgroups-1
            accx(i) = ieor( accx(i), nmh_readle32( p(i*4:) ) )
            accy(i) = ieor( accy(i), nmh_readle32( p(i*4+nbgroups*4:) ) )
            accx(i) = accx(i) + accy(i)
            accy(i) = ieor( accy(i), ishft(accx(i),  -1) )
            dummy1 = transfer( accx(i), 0_int16, 2 )
            dummy2 = transfer( nmh_m1_v(i), 0_int16, 2 )
            dummy1 = dummy1 * dummy2
            accx(i) = transfer( dummy1, 0_int32 )
            accx(i) = ieor( accx(i), ieor( ishft(accx(i), 5), &
                                           ishft(accx(i),-13) ) )
            dummy1 = transfer( accx(i), 0_int16, 2 )
            dummy2 = transfer( nmh_m2_v(i), 0_int16, 2 )
            dummy1 = dummy1 * dummy2
            accx(i) = transfer( dummy1, 0_int32 )
            accx(i) = ieor( accx(i), accy(i) )
            accx(i) = ieor( accx(i), ieor( ishft(accx(i), 11), &
                                           ishft(accx(i),-9) ) )
            dummy1 = transfer( accx(i), 0_int16, 2 )
            dummy2 = transfer( nmh_m3_v(i), 0_int16, 2 )
            dummy1 = dummy1 * dummy2
            accx(i) = transfer( dummy1, 0_int32 )
            accx(i) = ieor( accx(i), ieor( ishft(accx(i),-10), &
                                           ishft(accx(i),-20) ) )
        end do

    end subroutine nmhash32_long_round

    pure function nmhash32_long( p, seed ) result( sum )
        integer(int32) :: sum
        integer(int8), intent(in) :: p(0:)
        integer(int32), intent(in) :: seed

        integer(int32) :: accx(0:size(nmh_acc_init)-1)
        integer(int32) :: accy(0:size(nmh_acc_init)-1)
        integer(int64) :: nbrounds
        integer(int64) :: len
        integer(int32) :: len32(0:1)
        integer(int64) :: i

        len  = size( p, kind=int64 )
        nbrounds = (len-1) / ( 4*size(accx, kind=int64) * 2 )
        sum = 0

!  Init
        do i=0_int64, size(nmh_acc_init, kind=int64)-1
            accx(i) = nmh_acc_init(i)
            accy(i) = seed
        end do

        ! init
        do i=0_int64, nbrounds-1
            call nmhash32_long_round( accx, accy, &
                                      p(i*8*size(accx, kind=int64):) )
        end do
        call nmhash32_long_round( accx, accy, &
                                  p(len-8*size(accx, kind=int64):) )

        ! merge acc
        do i=0, size( accx, kind=int64 )-1
            accx(i) = ieor( accx(i), nmh_acc_init(i) )
            sum = sum + accx(i)
        end do

        len32 = transfer(len, 0_int32, 2)
        if ( little_endian ) then
            sum = sum + len32(1)
            sum = ieor(sum, len32(0))
        else
            sum = sum + len32(0)
            sum = ieor(sum, len32(1))
        end if

    end function nmhash32_long

    pure function nmhash32_avalanche32( x ) result( u32 )
        integer(int32) :: u32
        integer(int32), intent(in) :: x

        integer(int16) :: u16(0:1)
        integer(int32), parameter:: m1 = int(z'CCE5196D', int32)
        integer(int32), parameter:: m2 = int(z'464BE229', int32)
!       Due to an issue with Intel OneAPI ifort 2021 (see  
!       https://community.intel.com/t5/Intel-Fortran-Compiler/Intrinsic-transfer-with-a-provided-size-un-expected-behavior/m-p/1343313#M158733
!       ), it is not possible to define the following variables as a parameter.
        !integer(int16), parameter:: m1_16(0:1) = transfer(m1, 0_int16, 2)
        !integer(int16), parameter:: m2_16(0:1) = transfer(m2, 0_int16, 2)
        integer(int16) :: m1_16(0:1), m2_16(0:1)
        ! [-21 -8 cce5196d 12 -7 464be229 -21 -8] = 3.2267098842182733

        m1_16(0:1) = transfer(m1, 0_int16, 2)
        m2_16(0:1) = transfer(m2, 0_int16, 2)

        u32 = x
        u32 = ieor( u32, ieor( ishft( u32, -8 ), ishft( u32, -21 ) ) )
        u16 = transfer( u32, 0_int16, 2 )
        u16(0) = u16(0) * m1_16(0)
        u16(1) = u16(1) * m1_16(1)
        u32 = transfer( u16, 0_int32 )
        u32 = ieor( u32, ieor( ishft( u32, 12 ), ishft( u32, -7 ) ) )
        u16 = transfer( u32, 0_int16, 2 )
        u16(0) = u16(0) * m2_16(0)
        u16(1) = u16(1) * m2_16(1)
        u32 = transfer( u16, 0_int32 )
        u32 = ieor( u32, ieor( ishft( u32, -8 ), ishft( u32, -21 ) ) )

    end function nmhash32_avalanche32

    pure module function int8_nmhash32( key, seed ) result( hash )
!! NMHASH32 hash function for rank 1 array keys of kind INT8
        integer(int32) :: hash
        integer(int8), intent(in) :: key(0:)
        integer(int32), intent(in) :: seed
        integer(int64) :: len
        integer(int32) :: u32
        integer(int16) :: u16(0:1)
        integer(int32) :: x, y
        integer(int32) :: new_seed

        len = size( key, kind=int64 )
        if ( len <= 32 ) then
            if ( len > 8 ) then
                hash = nmhash32_9to32( key, seed )
                return
            else if ( len > 4 ) then
                x = nmh_readle32(key)
                y = ieor( nmh_readle32(key(len-4:)), nmh_prime32_4 + 2 + seed )
                x = x + y
                x = ieor( x, ishft(x, len + 7 ) )
                hash = nmhash32_0to8( x, ishftc(y, 5) )
                return
            else
                select case(len)
                case(0)
                    new_seed = seed + nmh_prime32_2
                    u32 = 0
                case(1)
                    new_seed = seed + nmh_prime32_2 + ishft(1_int32, 24) + &
                               2_int32
                    if ( little_endian ) then
                        u32 = transfer( [key(0), 0_int8, 0_int8, 0_int8], &
                                        0_int32 )
                    else
                        u32 = transfer( [0_int8, 0_int8, 0_int8, key(0)], &
                                        0_int32 )
                    end if
                case(2)
                    new_seed = seed + nmh_prime32_2 + ishft(2_int32, 24) + &
                               4_int32
                    if (little_endian) then
                        u32 = transfer( [nmh_readle16(key), 0_int16], 0_int32 )
                    else
                        u32 = transfer( [0_int16, nmh_readle16(key)], 0_int32 )
                    end if
                case(3)
                    new_seed = seed + nmh_prime32_2 + ishft(3_int32, 24) + &
                               6_int32
                    if ( little_endian ) then
                        u16(1) = transfer( [key(2), 0_int8], 0_int16 )
                        u16(0) = nmh_readle16( key )
                    else
                        u16(0) = transfer( [0_int8, key(2)], 0_int16 )
                        u16(1) = nmh_readle16( key )
                    end if
                    u32 = transfer( u16, 0_int32 )
                case(4)
                    new_seed = seed + nmh_prime32_3
                    u32 = nmh_readle32(key)
                case default
                    hash = 0
                    return
                end select
                hash = nmhash32_0to8(u32+new_seed, ishftc(new_seed, 5) )
                return
            end if
        else if ( len < 256_int64 ) then
            hash = nmhash32_33to255( key, seed )
            return
        else
            hash = nmhash32_avalanche32( nmhash32_long(key, seed ))
            return
        end if

    end function int8_nmhash32

    pure function nmhash32x_0to4( x, seed ) result( hash )
        integer(int32), intent(in) :: x
        integer(int32), intent(in) :: seed
        integer(int32) :: hash

        ! [bdab1ea9 18 a7896a1b 12 83796a2d 16] = 0.092922873297662509

        hash = x
        hash = ieor( hash, seed )
        hash = hash * int(z'BDAB1EA9', int32)
        hash = hash + ishftc(seed, 31)
        hash = ieor( hash, ishft(hash, -18) )
        hash = hash * int(z'A7896A1B', int32)
        hash = ieor( hash, ishft(hash, -12) )
        hash = hash * int(z'83796A2D', int32)
        hash = ieor( hash, ishft(hash, -16) )

    end function nmhash32x_0to4

    pure function nmhash32x_5to8( p, seed ) result( x )
        integer(int8), intent(in) :: p(0:)
        integer(int32), intent(in) :: seed
        integer(int32) :: x

        integer(int64) :: len
        integer(int32) :: y

        ! 5 to 9 bytes
        ! mixer: [11049a7d 23 bcccdc7b 12 065e9dad 12] = 0.16577596555667246

        len = size(p, kind=int64)
        x = ieor( nmh_readle32(p), nmh_prime32_3 )
        y = ieor( nmh_readle32(p(len-4:)), seed )
        x  = x + y
        x = ieor( x, ishft(x, -len) )
        x = x * int(z'11049A7D', int32)
        x = ieor( x, ishft(x, -23) )
        x = x * int(z'BCCCDC7B', int32)
        x = ieor( x, ishftc(y, 3) )
        x = ieor( x, ishft(x, -12) )
        x = x * int(z'065E9DAD', int32)
        x = ieor( x, ishft(x, -12) )

    end function nmhash32x_5to8

    pure function nmhash32x_9to255( p, seed ) result( x )
        integer(int8), intent(in) :: p(0:)
        integer(int32), intent(in) :: seed
        integer(int32) :: x

        integer(int64) :: len
        integer(int32) :: len32(0:1), len_base
        integer(int32) :: y
        integer(int32) :: a, b
        integer(int64) :: i, r

        ! - at least 9 bytes
        ! - base mixer: [11049a7d 23 bcccdc7b 12 065e9dad 12] = 0.16577596555667246
        ! - tail mixer: [16 a52fb2cd 15 551e4d49 16] = 0.17162579707098322

        len = size(p, kind=int64)
        len32 = transfer(len, 0_int32, 2)
        if (little_endian) then
            len_base = len32(0)
        else
            len_base = len32(1)
        end if
        x = nmh_prime32_3
        y = seed
        a = nmh_prime32_4
        b = seed
        r = (len - 1)/16

        do i=0, r-1
            x = ieor(x, nmh_readle32( p(i*16 + 0:) ) )
            y = ieor(y, nmh_readle32( p(i*16 + 4:) ) )
            x = ieor(x, y)
            x = x * int(z'11049A7D', int32)
            x = ieor(x, ishft(x, -23) )
            x = x * int(z'BCCCDC7B', int32)
            y = ishftc(y, 4)
            x = ieor(x, y)
            x = ieor(x, ishft(x, -12) )
            x = x * int(z'065E9DAD', int32)
            x = ieor(x, ishft(x, -12) )

            a = ieor(a, nmh_readle32(p(i*16 + 8:)))
            b = ieor(b, nmh_readle32(p(i*16 + 12:)))
            a = ieor(a, b)
            a = a * int(z'11049A7D', int32)
            a = ieor(a, ishft(a, -23) )
            a = a * int(z'BCCCDC7B', int32)
            b = ishftc(b, 3)
            a = ieor(a, b)
            a = ieor(a, ishft(a, -12) )
            a = a * int(z'065E9DAD', int32)
            a = ieor(a, ishft(a, -12) )
        end do

        if ( iand(len_base-1_int32, 8_int32) /= 0 ) then
            if ( iand(len_base-1_int32, 4_int32) /= 0 ) then
                a = ieor( a, nmh_readle32( p(r*16 + 0:) ) )
                b = ieor( b, nmh_readle32( p(r*16 + 4:) ) )
                a = ieor(a, b)
                a = a * int(z'11049A7D', int32)
                a = ieor(a, ishft(a, -23) )
                a = a * int(z'BCCCDC7B', int32)
                a = ieor(a, ishftc(b, 4))
                a = ieor(a, ishft(a, -12))
                a = a * int(z'065E9DAD', int32)
            else
                a = ieor( a, nmh_readle32( p(r*16:) ) + b )
                a = ieor( a, ishft(a, -16) )
                a = a * int(z'A52FB2CD', int32)
                a = ieor( a, ishft(a, -15) )
                a = a * int(z'551E4D49', int32)
            end if
            x = ieor( x, nmh_readle32( p(len - 8:) ) )
            y = ieor( y, nmh_readle32( p(len - 4:) ) )
            x = ieor( x, y )
            x = x * int(z'11049A7D', int32)
            x = ieor( x, ishft(x, -23) )
            x = x * int(z'BCCCDC7B', int32);
            x = ieor( x, ishftc(y, 3) )
            x = ieor( x, ishft(x, -12) )
            x = x * int(z'065E9DAD', int32)
        else
            if ( iand(len_base-1_int32, 4_int32) /= 0) then
                a = ieor(a, nmh_readle32(p( r * 16:) ) + b )
                a = ieor( a, ishft(a,-16) )
                a = a * int(z'A52FB2CD', int32)
                a = ieor( a, ishft(a,-15) )
                a = a * int(z'551E4D49', int32)
            end if
            x = ieor( x, nmh_readle32(p( len - 4:) ) + y )
            x = ieor( x, ishft(x,-16) )
            x = x * int(z'A52FB2CD', int32)
            x = ieor( x, ishft(x,-15) )
            x = x * int(z'551E4D49', int32)
        end if

        x = ieor(x, len_base )
        x = ieor(x, ishftc(a, 27)) ! rotate one lane to pass Diff test
        x = ieor(x, ishft(x,-14))
        x = x * int(z'141CC535', int32 )

    end function nmhash32x_9to255

    pure function nmhash32x_avalanche32( x ) result(hash)
        integer(int32) :: hash
        integer(int32), intent(in) :: x
! Mixer with 2 mul from skeeto/hash-prospector:
! [15 d168aaad 15 af723597 15] = 0.15983776156606694

        hash = x
        hash = ieor( hash, ishft( hash, -15 ) )
        hash = hash * int( z'D168AAAD', int32 )
        hash = ieor( hash, ishft( hash, -15 ) )
        hash = hash * int( z'AF723597', int32 )
        hash = ieor( hash, ishft( hash, -15 ) )

    end function nmhash32x_avalanche32

    pure module function int8_nmhash32x( key, seed ) result(hash)
!! NMHASH32x hash function for rank 1 array keys of kind INT8
        integer(int32) :: hash
        integer(int8), intent(in) :: key(0:)
        integer(int32), intent(in) :: seed

        integer(int64) :: len
        integer(int32) :: seed2
        integer(int32) :: u32
        integer(int16) :: u16(0:1)

        len = size( key, kind=int64 )
        if ( len <= 8 ) then
            if ( len > 4 ) then
                hash = nmhash32x_5to8( key, seed )
                return
            else ! 0 to 4 bytes
                select case (len)
                case(0)
                    seed2 = seed + nmh_prime32_2
                    u32 = 0
                case(1)
                    seed2 = seed + nmh_prime32_2 + ishft(1_int32, 24) + &
                        ishft(1_int32, 1)
                    if (little_endian) then
                        u32 = transfer( [key(0), 0_int8, 0_int8, 0_int8], &
                                        0_int32 )
                    else
                        u32 = transfer( [0_int8, 0_int8, 0_int8, key(0)], &
                                        0_int32 )
                    end if
                case(2)
                    seed2 = seed + nmh_prime32_2 + ishft(2_int32, 24) + &
                        ishft(2_int32, 1)
                    if (little_endian) then
                        u32 = transfer( [nmh_readle16(key), 0_int16], 0_int32 )
                    else
                        u32 = transfer( [0_int16, nmh_readle16(key)], 0_int32 )
                    end if
                case(3)
                    seed2 = seed + nmh_prime32_2 + ishft(3_int32, 24) + &
                        ishft(3_int32, 1)
                    if (little_endian ) then
                        u16(1) = transfer( [ key(2), 0_int8 ], 0_int16 )
                        u16(0) = nmh_readle16(key)
                    else
                        u16(0) = transfer( [ 0_int8, key(2) ], 0_int16 )
                        u16(1) = nmh_readle16(key)
                    end if
                    u32 = transfer( u16, 0_int32 )
                case(4)
                    seed2 = seed + nmh_prime32_1
                    u32 = nmh_readle32(key)
                case default
                    hash = 0
                    return
                end select
                hash = nmhash32x_0to4(u32, seed2)
                return
            end if
        end if
        if (len < 256) then
            hash = nmhash32x_9to255(key, seed)
            return
        end if
        hash = nmhash32x_avalanche32(nmhash32_long(key, seed))

    end function int8_nmhash32x

#:for k1 in INT_KINDS
    pure module function ${k1}$_nmhash32( key, seed ) result(hash_code)
!! NMHASH32 hash function for rank 1 array keys of kind ${k1}$
        integer(${k1}$), intent(in) :: key(:)
        integer(int32), intent(in)  :: seed
        integer(int32)           :: hash_code

        hash_code = int8_nmhash32( transfer( key, 0_int8, &
                     bytes_${k1}$*size(key, kind=int64) ), seed)

    end function ${k1}$_nmhash32

#:endfor

    elemental module function character_nmhash32( key, seed ) result(hash_code)
!! NMHASH32 hash function for default character keys
        character(*), intent(in)   :: key
        integer(int32), intent(in) :: seed
        integer(int32)             :: hash_code

        hash_code = int8_nmhash32( transfer( key, 0_int8, &
                     bytes_char*len(key, kind=int64) ), seed)

    end function character_nmhash32

#:for k1 in INT_KINDS
    pure module function ${k1}$_nmhash32x( key, seed ) result(hash_code)
!! NMHASH32X hash function for rank 1 array keys of kind ${k1}$
        integer(${k1}$), intent(in) :: key(:)
        integer(int32), intent(in)  :: seed
        integer(int32)           :: hash_code

        hash_code = int8_nmhash32x( transfer( key, 0_int8, &
                     bytes_${k1}$*size(key, kind=int64) ), seed)

    end function ${k1}$_nmhash32x

#:endfor

    elemental module function character_nmhash32x( key, seed ) result(hash_code)
!! NMHASH32X hash function for default character keys
        character(*), intent(in)   :: key
        integer(int32), intent(in) :: seed
        integer(int32)             :: hash_code

        hash_code = int8_nmhash32x( transfer( key, 0_int8, &
                     bytes_char*len(key, kind=int64) ), seed)

    end function character_nmhash32x

    module subroutine new_nmhash32_seed( seed )
! Random SEED generator for NMHASH32
        integer(int32), intent(inout) :: seed

        integer(int32) :: old_seed
        real(dp) :: sample

        old_seed = seed
        find_seed:do
            call random_number( sample )
            seed = int( floor( sample * 2_int64**32, int64 ) - 2_int64**31, &
                int32 )
            if ( seed /= old_seed ) return
        end do find_seed

    end subroutine new_nmhash32_seed

    module subroutine new_nmhash32x_seed( seed )
! Random SEED generator for NMHASH32X
         integer(int32), intent(inout) :: seed

        integer(int32) :: old_seed
        real(dp) :: sample

        old_seed = seed
        find_seed:do
            call random_number( sample )
            seed = int( floor( sample * 2_int64**32, int64 ) - 2_int64**31, &
                int32 )
            if ( seed /= old_seed ) return
        end do find_seed

    end subroutine new_nmhash32x_seed

end submodule stdlib_hash_32bit_nm