!> Module to generate random numbers
module MW_random
   use MW_kinds, only: wp
   implicit none
   private

   ! Public subroutines
   ! ------------------
   public :: seed
   public :: uniform_random_number
   public :: normal_random_number

   integer, save :: ix1, ix2, ix3
   real(wp), save :: field(100) !< field of random numbers

   integer, parameter :: m1=259200, ia1=7141, ic1=54773
   integer, parameter :: m2=134456, ia2=8121, ic2=28411
   integer, parameter :: m3=243000, ia3=4561, ic3=51349
   real(wp), parameter :: rm1 = 1.0_wp / real(m1,wp)
   real(wp), parameter :: rm2 = 1.0_wp / real(m2,wp)

contains

   ! ========================================================================
   !> Seed the pseudorandom number generator
   subroutine seed()
      implicit none
      integer :: j
      ix1=mod(ic1+1,m1)
      ix1=mod(ia1*ix1+ic1,m1)
      ix2=mod(ix1,m2)
      ix1=mod(ia1*ix1+ic1,m1)
      ix3=mod(ix1,m3)
      do j=1,97
         ix1=mod(ia1*ix1+ic1,m1)
         ix2=mod(ia2*ix2+ic2,m2)
         field(j)=(real(ix1,wp) + real(ix2,wp)*rm2)*rm1
      end do
   end subroutine seed

   ! ========================================================================
   !> Generate a uniformly distributed pseudorandom number with real values
   !! in the interval (0,1).
   subroutine uniform_random_number(harvest)
      implicit none
      ! Parameters
      ! ----------
      real(wp), intent(out) :: harvest
      integer :: j

      ix1=mod(ia1*ix1+ic1,m1)
      ix2=mod(ia2*ix2+ic2,m2)
      ix3=mod(ia3*ix3+ic3,m3)

      j=1+(97*ix3)/m3
      harvest = field(j)
      field(j) = (real(ix1,wp) + real(ix2,wp) * rm2) * rm1

   end subroutine uniform_random_number

   ! ========================================================================
   !> Generate a normally distributed pseudorandom number with zero mean and
   !! unit variance
   subroutine normal_random_number(harvest)
      implicit none
      ! Parameters
      ! ----------
      real(wp), intent(out) :: harvest

      ! locals
      ! ------
      real(wp) :: x, xj, r, rsq
      integer :: j

      real(wp), parameter :: a1 = 3.949846138_wp
      real(wp), parameter :: a3 = 0.252408784_wp
      real(wp), parameter :: a5 = 0.076542912_wp
      real(wp), parameter :: a7 = 0.008355968_wp
      real(wp), parameter :: a9 = 0.029899776_wp

      x = 0.0_wp
      do j=1,12
         call uniform_random_number(xj)
         x = x + xj
      enddo

      r = (x - 6.0_wp) / 4.0_wp
      rsq=r*r
      harvest = ((((a9*rsq+a7)*rsq+a5)*rsq+a3)*rsq+a1)*r

   end subroutine normal_random_number

end module MW_random
