module classes_tower_sampler use, intrinsic :: iso_fortran_env, only: DP => REAL64 use procedures_errors, only: error_exit use procedures_checks, only: check_positive implicit none private type, abstract, public :: Abstract_Tower_Sampler private logical :: unique_candidate = .true. integer :: i_unique_candidate = 0 integer :: num_candidates = 0 integer :: num_choices = 0 real(DP), allocatable :: limits(:) contains procedure :: construct => Abstract_construct procedure :: destroy => Abstract_destroy procedure :: reset => Abstract_reset procedure :: get_num_choices => Abstract_get_num_choices procedure :: get => Abstract_get end type Abstract_Tower_Sampler type, extends(Abstract_Tower_Sampler), public :: Concrete_Tower_Sampler end type Concrete_Tower_Sampler type, extends(Abstract_Tower_Sampler), public :: Null_Tower_Sampler contains procedure :: construct => Null_construct procedure :: destroy => Null_destroy procedure :: reset => Null_reset procedure :: get_num_choices => Null_get_num_choices procedure :: get => Null_get end type Null_Tower_Sampler contains !implementation Abstract_Tower_Sampler subroutine Abstract_construct(this, num_candidates) class(Abstract_Tower_Sampler), intent(out) :: this integer, intent(in) :: num_candidates call check_positive("Abstract_Tower_Sampler: construct", "num_candidates", num_candidates) this%num_candidates = num_candidates allocate(this%limits(this%num_candidates + 1)) this%limits = 0 end subroutine Abstract_construct !> Sets the ``stories'' of the tower. subroutine Abstract_reset(this, nums_candidates) class(Abstract_Tower_Sampler), intent(inout) :: this integer, intent(in) :: nums_candidates(:) real(DP) :: cumulative_weight(this%num_candidates) integer :: i_candidate if (size(nums_candidates) /= this%num_candidates) then call error_exit("Abstract_Tower_Sampler: set: size(nums_candidates) and "//& "this%num_candidates are different.") end if call check_positive("Abstract_Tower_Sampler: reset", "sum(nums_candidates)", & sum(nums_candidates)) this%num_choices = sum(nums_candidates) if (count(nums_candidates > 0) == 1) then this%unique_candidate = .true. this%i_unique_candidate = maxloc(nums_candidates, 1) else this%unique_candidate = .false. if (this%num_choices /= 0) then do i_candidate = 1, this%num_candidates cumulative_weight(i_candidate) = real(sum(nums_candidates(1:i_candidate)), DP)/& real(this%num_choices, DP) end do else cumulative_weight = 0._DP end if this%limits(1) = 0._DP this%limits(2:size(this%limits)) = cumulative_weight end if end subroutine Abstract_reset subroutine Abstract_destroy(this) class(Abstract_Tower_Sampler), intent(inout) :: this if (allocated(this%limits)) deallocate(this%limits) end subroutine Abstract_destroy pure integer function Abstract_get_num_choices(this) result(num_choices) class(Abstract_Tower_Sampler), intent(in) :: this num_choices = this%num_choices end function Abstract_get_num_choices integer function Abstract_get(this) result(i_random_candidate) class(Abstract_Tower_Sampler), intent(in) :: this integer :: i_candidate real(DP) :: rand if (this%unique_candidate) then i_random_candidate = this%i_unique_candidate return end if call random_number(rand) do i_candidate = 1, this%num_candidates if (this%limits(i_candidate) <= rand .and. rand < this%limits(i_candidate + 1)) then i_random_candidate = i_candidate return end if end do end function Abstract_get !end implementation Abstract_Tower_Sampler !implementation Null_Tower_Sampler subroutine Null_construct(this, num_candidates) class(Null_Tower_Sampler), intent(out) :: this integer, intent(in) :: num_candidates end subroutine Null_construct subroutine Null_destroy(this) class(Null_Tower_Sampler), intent(inout) :: this end subroutine Null_destroy subroutine Null_reset(this, nums_candidates) class(Null_Tower_Sampler), intent(inout) :: this integer, intent(in) :: nums_candidates(:) end subroutine Null_reset pure integer function Null_get_num_choices(this) result(num_choices) class(Null_Tower_Sampler), intent(in) :: this num_choices = 0 end function Null_get_num_choices integer function Null_get(this) result(i_random_candidate) class(Null_Tower_Sampler), intent(in) :: this i_random_candidate = 0 end function Null_get !end implementation Null_Tower_Sampler end module classes_tower_sampler