#:include "common.fypp" #:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES #:set ALL_KINDS_TYPES = INT_KINDS_TYPES + RC_KINDS_TYPES module stdlib_stats_distribution_uniform use stdlib_kinds, only : sp, dp, xdp, qp, int8, int16, int32, int64 use stdlib_error, only : error_stop use stdlib_random, only : dist_rand implicit none private real(dp), parameter :: MESENNE_NUMBER = 1.0_dp / (2.0_dp ** 53 - 1.0_dp) integer(int64), parameter :: INT_ONE = 1_int64 public :: rvs_uniform public :: pdf_uniform public :: cdf_uniform public :: shuffle interface rvs_uniform !! version: experimental !! !! Get uniformly distributed random variate for integer, real and complex !! variables. !! ([Specification](../page/specs/stdlib_stats_distribution_uniform.html# !! rvs_uniform-uniform-distribution-random-variates)) module procedure rvs_unif_0_rsp ! 0 dummy variable #:for k1, t1 in ALL_KINDS_TYPES module procedure rvs_unif_1_${t1[0]}$${k1}$ ! 1 dummy variable #:endfor #:for k1, t1 in ALL_KINDS_TYPES module procedure rvs_unif_${t1[0]}$${k1}$ ! 2 dummy variables #:endfor #:for k1, t1 in ALL_KINDS_TYPES module procedure rvs_unif_array_${t1[0]}$${k1}$ ! 3 dummy variables #:endfor end interface rvs_uniform interface pdf_uniform !! version: experimental !! !! Get uniform distribution probability density (pdf) for integer, real and !! complex variables. !! ([Specification](../page/specs/stdlib_stats_distribution_uniform.html# !! pdf_uniform-uniform-probability-density-function)) #:for k1, t1 in ALL_KINDS_TYPES module procedure pdf_unif_${t1[0]}$${k1}$ #:endfor end interface pdf_uniform interface cdf_uniform !! version: experimental !! !! Get uniform distribution cumulative distribution function (cdf) for integer, !! real and complex variables. !! ([Specification](../page/specs/stdlib_stats_distribution_uniform.html# !! cdf_uniform-uniform-cumulative-distribution-function)) !! #:for k1, t1 in ALL_KINDS_TYPES module procedure cdf_unif_${t1[0]}$${k1}$ #:endfor end interface cdf_uniform interface shuffle !! version: experimental !! !! Fisher-Yates shuffle algorithm for a rank one array of integer, real and !! complex variables. !! ([Specification](../page/specs/stdlib_stats_distribution_uniform.html# !! shuffle-using-fisher-yates-algorithm-to-generate-a-random-permutation-of-a-list)) !! #:for k1, t1 in ALL_KINDS_TYPES module procedure shuffle_${t1[0]}$${k1}$ #:endfor end interface shuffle contains #:for k1, t1 in INT_KINDS_TYPES impure elemental function rvs_unif_1_${t1[0]}$${k1}$(scale) result(res) ! ! Uniformly distributed integer in [0, scale] ! Bitmask with rejection ! https://www.pcg-random.org/posts/bounded-rands.html ! ! Fortran 90 translated from c by Jim-215-fisher ! ${t1}$, intent(in) :: scale ${t1}$ :: res, u, mask integer :: zeros, bits_left, bits if(scale <= 0_${k1}$) call error_stop("Error(rvs_unif_1): Uniform" & //" distribution scale parameter must be positive") zeros = leadz(scale) bits = bit_size(scale) - zeros mask = shiftr(not(0_${k1}$), zeros) L1 : do u = dist_rand(scale) res = iand(u, mask) if(res <= scale) exit L1 bits_left = zeros L2 : do if(bits_left < bits) exit L2 u = shiftr(u, bits) res = iand(u, mask) if(res <= scale) exit L1 bits_left = bits_left - bits end do L2 end do L1 end function rvs_unif_1_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in INT_KINDS_TYPES impure elemental function rvs_unif_${t1[0]}$${k1}$(loc, scale) result(res) ! ! Uniformly distributed integer in [loc, loc + scale] ! ${t1}$, intent(in) :: loc, scale ${t1}$ :: res if(scale <= 0_${k1}$) call error_stop("Error(rvs_unif): Uniform" & //" distribution scale parameter must be positive") res = loc + rvs_unif_1_${t1[0]}$${k1}$(scale) end function rvs_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in REAL_KINDS_TYPES impure elemental function rvs_unif_0_${t1[0]}$${k1}$( ) result(res) ! ! Uniformly distributed float in [0,1] ! Based on the paper by Frederic Goualard, "Generating Random Floating- ! Point Numbers By Dividing Integers: a Case Study", Proceedings of ! ICCS 2020, June 2020, Amsterdam, Netherlands ! ${t1}$ :: res integer(int64) :: tmp tmp = shiftr(dist_rand(INT_ONE), 11) ! Get random from [0,2^53-1] res = real(tmp * MESENNE_NUMBER, kind = ${k1}$) ! convert to [0,1] end function rvs_unif_0_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in REAL_KINDS_TYPES impure elemental function rvs_unif_1_${t1[0]}$${k1}$(scale) result(res) ! ! Uniformly distributed float in [0, scale] ! ${t1}$, intent(in) :: scale ${t1}$ :: res if(scale == 0._${k1}$) call error_stop("Error(rvs_unif_1): " & //"Uniform distribution scale parameter must be non-zero") res = scale * rvs_unif_0_${t1[0]}$${k1}$( ) end function rvs_unif_1_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in REAL_KINDS_TYPES impure elemental function rvs_unif_${t1[0]}$${k1}$(loc, scale) result(res) ! ! Uniformly distributed float in [loc, loc + scale] ! ${t1}$, intent(in) :: loc, scale ${t1}$ :: res if(scale == 0._${k1}$) call error_stop("Error(rvs_unif): " & //"Uniform distribution scale parameter must be non-zero") res = loc + scale * rvs_unif_0_${t1[0]}$${k1}$( ) end function rvs_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in CMPLX_KINDS_TYPES impure elemental function rvs_unif_1_${t1[0]}$${k1}$(scale) result(res) ! ! Uniformly distributed complex in [(0,0i), (scale, i(scale))] ! The real part and imaginary part are independent of each other, so that ! the joint distribution is on an unit square [(0,0i), (scale,i(scale))] ! ${t1}$, intent(in) :: scale ${t1}$ :: res real(${k1}$) :: r1, tr, ti if(scale == (0.0_${k1}$, 0.0_${k1}$)) call error_stop("Error(rvs_uni_" & //"1): Uniform distribution scale parameter must be non-zero") r1 = rvs_unif_0_r${k1}$( ) if(scale % re == 0.0_${k1}$) then ti = scale % im * r1 tr = 0.0_${k1}$ else if(scale % im == 0.0_${k1}$) then tr = scale % re * r1 ti = 0.0_${k1}$ else tr = scale % re * r1 r1 = rvs_unif_0_r${k1}$( ) ti = scale % im * r1 end if res = cmplx(tr, ti, kind=${k1}$) end function rvs_unif_1_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in CMPLX_KINDS_TYPES impure elemental function rvs_unif_${t1[0]}$${k1}$(loc, scale) result(res) ! ! Uniformly distributed complex in [(loc,iloc), (loc + scale, i(loc + ! scale))]. ! The real part and imaginary part are independent of each other, so that ! the joint distribution is on an unit square [(loc,iloc), (loc + scale, ! i(loc + scale))] ! ${t1}$, intent(in) :: loc, scale ${t1}$ :: res real(${k1}$) :: r1, tr, ti if(scale == (0.0_${k1}$, 0.0_${k1}$)) call error_stop("Error(rvs_uni_" & //"): Uniform distribution scale parameter must be non-zero") r1 = rvs_unif_0_r${k1}$( ) if(scale % re == 0.0_${k1}$) then tr = loc % re ti = loc % im + scale % im * r1 else if(scale % im == 0.0_${k1}$) then tr = loc % re + scale % re * r1 ti = loc % im else tr = loc % re + scale % re * r1 r1 = rvs_unif_0_r${k1}$( ) ti = loc % im + scale % im * r1 end if res = cmplx(tr, ti, kind=${k1}$) end function rvs_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in INT_KINDS_TYPES function rvs_unif_array_${t1[0]}$${k1}$(loc, scale, array_size) result(res) integer, intent(in) :: array_size ${t1}$, intent(in) :: loc, scale ${t1}$ :: res(array_size) ${t1}$ :: u, mask, nn integer :: i, zeros, bits_left, bits if(scale == 0_${k1}$) call error_stop("Error(rvs_unif_array): " & //"Uniform distribution scale parameter must be non-zero") zeros = leadz(scale) bits = bit_size(scale) - zeros mask = shiftr(not(0_${k1}$), zeros) do i = 1, array_size L1 : do u = dist_rand(scale) nn = iand(u, mask) if(nn <= scale) exit L1 bits_left = zeros L2 : do if(bits_left < bits) exit L2 u = shiftr(u, bits) nn = iand(u, mask) if(nn <= scale) exit L1 bits_left = bits_left - bits end do L2 end do L1 res(i) = loc + nn end do end function rvs_unif_array_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in REAL_KINDS_TYPES function rvs_unif_array_${t1[0]}$${k1}$(loc, scale, array_size) result(res) integer, intent(in) :: array_size ${t1}$, intent(in) :: loc, scale ${t1}$ :: res(array_size) ${t1}$ :: t integer(int64) :: tmp integer :: i if(scale == 0._${k1}$) call error_stop("Error(rvs_unif_array):" & //" Uniform distribution scale parameter must be non-zero") do i = 1, array_size tmp = shiftr(dist_rand(INT_ONE), 11) t = real(tmp * MESENNE_NUMBER, kind = ${k1}$) res(i) = loc + scale * t end do end function rvs_unif_array_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in CMPLX_KINDS_TYPES function rvs_unif_array_${t1[0]}$${k1}$(loc, scale, array_size) result(res) integer, intent(in) :: array_size ${t1}$, intent(in) :: loc, scale ${t1}$ :: res(array_size) real(${k1}$) :: r1, tr, ti integer(int64) :: tmp integer :: i if(scale == (0.0_${k1}$, 0.0_${k1}$)) call error_stop("Error(rvs_unif" & //"_array): Uniform distribution scale parameter must be non-zero") do i = 1, array_size tmp = shiftr(dist_rand(INT_ONE), 11) r1 = real(tmp * MESENNE_NUMBER, kind = ${k1}$) if(scale % re == 0.0_${k1}$) then tr = loc % re ti = loc % im + scale % im * r1 else if(scale % im == 0.0_${k1}$) then tr = loc % re + scale % re * r1 ti = loc % im else tr = loc % re + scale % re * r1 tmp = shiftr(dist_rand(INT_ONE), 11) r1 = real(tmp * MESENNE_NUMBER, kind = ${k1}$) ti = loc % im + scale % im * r1 end if res(i) = cmplx(tr, ti, kind=${k1}$) end do end function rvs_unif_array_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in INT_KINDS_TYPES elemental function pdf_unif_${t1[0]}$${k1}$(x, loc, scale) result(res) ${t1}$, intent(in) :: x, loc, scale real :: res if(scale == 0_${k1}$) then res = 0.0 else if(x < loc .or. x > (loc + scale)) then res = 0.0 else res = 1. / (scale + 1_${k1}$) end if end function pdf_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in REAL_KINDS_TYPES elemental function pdf_unif_${t1[0]}$${k1}$(x, loc, scale) result(res) ${t1}$, intent(in) :: x, loc, scale ${t1}$ :: res ${t1}$, parameter :: zero = 0.0_${k1}$, one = 1.0_${k1}$ if(scale == zero) then res = zero else if(x < loc .or. x > (loc + scale)) then res = zero else res = one / scale end if end function pdf_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in CMPLX_KINDS_TYPES elemental function pdf_unif_${t1[0]}$${k1}$(x, loc, scale) result(res) ${t1}$, intent(in) :: x, loc, scale real(${k1}$) :: res, tr, ti real(${k1}$), parameter :: zero = 0.0_${k1}$, one = 1.0_${k1}$ tr = loc % re + scale % re; ti = loc % im + scale % im if(scale == (zero, zero)) then res = zero else if((x % re >= loc % re .and. x % re <= tr) .and. & (x % im >= loc % im .and. x % im <= ti)) then res = one / (scale % re * scale % im) else res = zero end if end function pdf_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in INT_KINDS_TYPES elemental function cdf_unif_${t1[0]}$${k1}$(x, loc, scale) result(res) ${t1}$, intent(in) :: x, loc, scale real :: res if(scale == 0_${k1}$) then res = 0.0 else if(x < loc) then res = 0.0 else if(x >= loc .and. x <= (loc + scale)) then res = real((x - loc + 1_${k1}$)) / real((scale + 1_${k1}$)) else res = 1.0 end if end function cdf_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in REAL_KINDS_TYPES elemental function cdf_unif_${t1[0]}$${k1}$(x, loc, scale) result(res) ${t1}$, intent(in) :: x, loc, scale ${t1}$ :: res ${t1}$, parameter :: zero = 0.0_${k1}$, one = 1.0_${k1}$ if(scale == zero) then res = zero else if(x < loc) then res = zero else if(x >= loc .and. x <= (loc + scale)) then res = (x - loc) / scale else res = one end if end function cdf_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in CMPLX_KINDS_TYPES elemental function cdf_unif_${t1[0]}$${k1}$(x, loc, scale) result(res) ${t1}$, intent(in) :: x, loc, scale real(${k1}$) :: res real(${k1}$), parameter :: zero = 0.0_${k1}$, one = 1.0_${k1}$ logical :: r1, r2, i1, i2 if(scale == (zero, zero)) then res = zero return end if r1 = x % re < loc % re r2 = x % re > (loc % re + scale % re) i1 = x % im < loc % im i2 = x % im > (loc % im + scale % im) if(r1 .or. i1) then res = zero else if((.not. r1) .and. (.not. r2) .and. i2) then res = (x % re - loc % re) / scale % re else if((.not. i1) .and. (.not. i2) .and. r2) then res = (x % im - loc % im) / scale % im else if((.not. r1) .and. (.not. r2) .and. (.not. i1) .and. (.not. i2)) & then res = ((x % re - loc % re) / scale % re) * ((x % im - loc % im) / & scale % im) else if(r2 .and. i2)then res = one end if end function cdf_unif_${t1[0]}$${k1}$ #:endfor #:for k1, t1 in ALL_KINDS_TYPES function shuffle_${t1[0]}$${k1}$( list ) result(res) ${t1}$, intent(in) :: list(:) ${t1}$ :: res(size(list)) ${t1}$ :: tmp integer :: n, i, j n = size(list) res = list do i = 1, n - 1 j = rvs_uniform(n - i) + i tmp = res(i) res(i) = res(j) res(j) = tmp end do end function shuffle_${t1[0]}$${k1}$ #:endfor end module stdlib_stats_distribution_uniform