!# This source file is distributed with code Pégase.3.0.1 (2019-02-21).
!# Copyright: Michel Fioc (Michel.Fioc@iap.fr), Sorbonne université, 
!# Institut d'astrophysique de Paris/CNRS, France.
!# 
!# Pégase.3.0.1 is governed by the CeCILL license under French law and abides 
!# by the rules of distribution of free software. You can use, modify and/or 
!# redistribute this software under the terms of the CeCILL license as circulated 
!# by CEA, CNRS and INRIA at "http://www.cecill.info". The text of this license
!# is also available in French and in English in directory "doc_dir/" of this
!# code.
!# 
!# As a counterpart to the access to the source code and to the rights to copy,
!# modify and redistribute it granted by the license, users are provided only
!# with a limited warranty, and the software's author, the holder of the
!# economic rights, and the successive licensors have only limited
!# liability. 
!# 
!# The fact that you are presently reading this means that you have had
!# knowledge of the CeCILL license and that you accept its terms.
!#====================================================================== 

module mod_random

  use mod_types
  implicit none
  private
  integer, save :: dim_seed
  integer, dimension(:), allocatable, save :: seed  

  public :: uniform_random, gaussian_random, initialize_seed, dim_seed, seed

contains

!#======================================================================

  function uniform_random(exclude_zero, exclude_one) 

!# Draws a random number uniformly distributed in [0, 1].
!# Exclude 0 if `exclude_zero` is present and true.
!# Exclude 1 if `exclude_one` is present and true.

    implicit none
    real(CDR) :: uniform_random
    logical, intent(in), optional :: exclude_zero, exclude_one
!#......................................................................
    logical :: exclude_zero_eff, exclude_one_eff
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    if (present(exclude_zero)) then
       exclude_zero_eff = exclude_zero
    else
       exclude_zero_eff = .false.
    endif
    if (present(exclude_one)) then
       exclude_one_eff = exclude_one
    else
       exclude_one_eff = .false.
    endif

    do
       call random_number(uniform_random)
       if (exclude_zero_eff .and. uniform_random == 0) cycle
       if (exclude_one_eff .and. uniform_random == 1) cycle
       exit
    enddo

  end function uniform_random

!#======================================================================

  function gaussian_random()

!# Draws a random number following a Gaussian distribution of mean 0 and
!# variance 1.

    implicit none
    real(CDR) :: gaussian_random
!#......................................................................
    real(CDR), dimension(2) :: random
    real(CDR) :: x
    real(CDR), save :: next
    logical, save :: exist_next = .false.
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    if (exist_next) then
       gaussian_random = next
       exist_next = .false.
    else
       do
          call random_number(random(:))
          random(:) = 2*random(:)-1
          x = sum(random(:)**2)
          if (x > 0 .and. x < 1) exit
       enddo
       x = sqrt(-2*log(x)/x)
       gaussian_random = random(1)*x
       next = random(2)*x
       exist_next = .true.
    endif

  end function gaussian_random

!#======================================================================

  subroutine initialize_seed

!# With gfortran (but not ifort), `call random_seed()` always provide
!# the same seeds. Use this routine instead.
    
    implicit none
!#......................................................................
    integer :: clock
    integer, dimension(dim_seed) :: seed2
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    call random_seed()
    call random_seed(get = seed)
    call random_seed()
    call random_seed(get = seed2)
    if (all(seed == seed2)) then !# gfortran.
       call system_clock(count = clock)
       
!# A standard way is to set `seed = clock + 37*(/(i_seed, i_seed = 0, dim_seed-1)/)`,
!# but the following seems safer.
       call random_seed(get = seed)
       seed = ieor(seed, clock)
       
       call random_seed(put = seed)
    end if
    
  end subroutine initialize_seed
  
end module mod_random
