Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
80 changes: 80 additions & 0 deletions src/stdlib_stats_distribution_normal.fypp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ module stdlib_stats_distribution_normal
#:for k1, t1 in RC_KINDS_TYPES
module procedure rvs_norm_array_${t1[0]}$${k1}$ !3 dummy variables
#:endfor

#:for k1, t1 in RC_KINDS_TYPES
module procedure rvs_norm_array_default_${t1[0]}$${k1}$ !2 dummy variables (mold, array_size)
Comment on lines +38 to +39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be included in the previous fypp loop (Line 35).

#:endfor
end interface rvs_normal

interface pdf_normal
Expand Down Expand Up @@ -238,6 +242,82 @@ contains

#:endfor

#:for k1, t1 in REAL_KINDS_TYPES
impure function rvs_norm_array_default_${t1[0]}$${k1}$ (mold, array_size) result(res)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
impure function rvs_norm_array_default_${t1[0]}$${k1}$ (mold, array_size) result(res)
impure function rvs_norm_array_default_${t1[0]}$${k1}$ (array_size, mold) result(res)

mold is usually the last argument.

!
! Standard normal array random variate with default loc=0, scale=1
! The mold argument is used only to determine the type and is not referenced
!
${t1}$, intent(in) :: mold
integer, intent(in) :: array_size
${t1}$ :: res(array_size)
${t1}$, parameter :: r = 3.442619855899_${k1}$, rr = 1.0_${k1}$/r
${t1}$ :: x, y, re
integer :: hz, iz, i

if (.not. zig_norm_initialized) call zigset

do i = 1, array_size
iz = 0
hz = dist_rand(1_int32)
iz = iand(hz, 127)
if (abs(hz) < kn(iz)) then
re = hz*wn(iz)
else
L1: do
L2: if (iz == 0) then
do
x = -log(uni(1.0_${k1}$))*rr
y = -log(uni(1.0_${k1}$))
if (y + y >= x*x) exit
end do
re = r + x
if (hz <= 0) re = -re
exit L1
end if L2
x = hz*wn(iz)
if (fn(iz) + uni(1.0_${k1}$)*(fn(iz - 1) - fn(iz)) < &
exp(-HALF*x*x)) then
re = x
exit L1
end if

hz = dist_rand(1_int32)
iz = iand(hz, 127)
if (abs(hz) < kn(iz)) then
re = hz*wn(iz)
exit L1
end if
end do L1
end if
res(i) = re ! Default: loc=0, scale=1, so re*1 + 0 = re
end do
Comment on lines +254 to +294
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
${t1}$, parameter :: r = 3.442619855899_${k1}$, rr = 1.0_${k1}$/r
${t1}$ :: x, y, re
integer :: hz, iz, i
if (.not. zig_norm_initialized) call zigset
do i = 1, array_size
iz = 0
hz = dist_rand(1_int32)
iz = iand(hz, 127)
if (abs(hz) < kn(iz)) then
re = hz*wn(iz)
else
L1: do
L2: if (iz == 0) then
do
x = -log(uni(1.0_${k1}$))*rr
y = -log(uni(1.0_${k1}$))
if (y + y >= x*x) exit
end do
re = r + x
if (hz <= 0) re = -re
exit L1
end if L2
x = hz*wn(iz)
if (fn(iz) + uni(1.0_${k1}$)*(fn(iz - 1) - fn(iz)) < &
exp(-HALF*x*x)) then
re = x
exit L1
end if
hz = dist_rand(1_int32)
iz = iand(hz, 127)
if (abs(hz) < kn(iz)) then
re = hz*wn(iz)
exit L1
end if
end do L1
end if
res(i) = re ! Default: loc=0, scale=1, so re*1 + 0 = re
end do
res = rvs_norm_array_default_${t1[0]}$${k1}$(0._${k1}, 1._${k1}$, array_size)

I propose to call the procedure, instead of repeating the code, except if there is a good reason to repeat it (I didn't look carefully to the code; if the code needs to be (partly) repeated, the reason should be provided in the comments)..

end function rvs_norm_array_default_${t1[0]}$${k1}$

#:endfor

#:for k1, t1 in CMPLX_KINDS_TYPES
impure function rvs_norm_array_default_${t1[0]}$${k1}$ (mold, array_size) result(res)
!
! Standard normal complex array random variate with default loc=0, scale=1
! The mold argument is used only to determine the type and is not referenced
!
${t1}$, intent(in) :: mold
integer, intent(in) :: array_size
integer :: i
${t1}$ :: res(array_size)
real(${k1}$) :: tr, ti

do i = 1, array_size
tr = rvs_norm_0_r${k1}$ ()
ti = rvs_norm_0_r${k1}$ ()
res(i) = cmplx(tr, ti, kind=${k1}$)
end do

end function rvs_norm_array_default_${t1[0]}$${k1}$

#:endfor

#:for k1, t1 in CMPLX_KINDS_TYPES
impure function rvs_norm_array_${t1[0]}$${k1}$ (loc, scale, array_size) result(res)
${t1}$, intent(in) :: loc, scale
Expand Down
40 changes: 40 additions & 0 deletions test/stats/test_distribution_normal.fypp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ program test_distribution_normal
call test_nor_rvs_${t1[0]}$${k1}$
#:endfor

#:for k1, t1 in RC_KINDS_TYPES
call test_nor_rvs_default_${t1[0]}$${k1}$
#:endfor



#:for k1, t1 in RC_KINDS_TYPES
Expand Down Expand Up @@ -138,6 +142,42 @@ contains
#:endfor


#:for k1, t1 in RC_KINDS_TYPES
subroutine test_nor_rvs_default_${t1[0]}$${k1}$
${t1}$ :: a1(10), a2(10), mold
integer :: i
integer :: seed, get

print *, "Test normal_distribution_rvs_default_${t1[0]}$${k1}$"
seed = 25836914
call random_seed(seed, get)

! explicit form with loc=0, scale=1
#:if t1[0] == "r"
a1 = nor_rvs(0.0_${k1}$, 1.0_${k1}$, 10)
#:else
a1 = nor_rvs((0.0_${k1}$, 0.0_${k1}$), (1.0_${k1}$, 1.0_${k1}$), 10)
#:endif

! reset seed to reproduce same random sequence
seed = 25836914
call random_seed(seed, get)

! default mold form: mold used only to disambiguate kind
#:if t1[0] == "r"
mold = 0.0_${k1}$
#:else
mold = (0.0_${k1}$, 0.0_${k1}$)
#:endif

a2 = nor_rvs(mold, 10)

call check(all(a1 == a2), msg="normal_distribution_rvs_default_${t1[0]}$${k1}$ failed", warn=warn)
end subroutine test_nor_rvs_default_${t1[0]}$${k1}$

#:endfor




#:for k1, t1 in RC_KINDS_TYPES
Expand Down