.. default-domain:: chpl

.. module:: NPBRandom
   :synopsis: NAS Parallel Benchmark RNG

NPBRandom
=========

NAS Parallel Benchmark RNG

The pseudorandom number generator (PRNG) implemented by
this module uses the algorithm from the NAS Parallel Benchmarks
(NPB, available at: http://www.nas.nasa.gov/publications/npb.html),
which can be used to generate random values of type `real(64)`,
`imag(64)`, and `complex(128)`.

Paraphrasing the comments from the NPB reference implementation:

  This generator returns uniform pseudorandom real values in the
  range (0, 1) by using the linear congruential generator

    `x_{k+1} = a x_k  (mod 2**46)`

  where 0 < x_k < 2**46 and 0 < a < 2**46.  This scheme
  generates 2**44 numbers before repeating.  The generated values
  are normalized to be between 0 and 1, i.e., 2**(-46) * x_k.

  This generator should produce the same results on any computer
  with at least 48 mantissa bits for `real(64)` data.

To generate `complex` elements, consecutive pairs of random numbers are
assigned to the real and imaginary components, respectively.

The values generated by this NPB RNG do not include 0.0 and 1.0.

We have tested this implementation with TestU01 (available at
http://simul.iro.umontreal.ca/testu01/tu01.html ). In our experiments with
TestU01 1.2.3 and the Crush suite (which consists of 144 statistical
tests), this NPB RNG failed 41/144 tests. As a linear congruential
generator, this RNG has known statistical problems that TestU01
was able to detect.

.. note::

  This module is currently restricted to generating `real(64)`,
  `imag(64)`, and `complex(128)` complex values.



.. class:: NPBRandomStream

   
   Models a stream of pseudorandom numbers.  See the module-level
   notes for :mod:`NPBRandom` for details on the PRNG used.
   


   .. attribute:: type eltType = real(64)

      
      Specifies the type of value generated by the NPBRandomStream.
      Currently, only `real(64)`, `imag(64)`, and `complex(128)` are
      supported.
      

   .. attribute:: param parSafe: bool = true

      
      Indicates whether or not the NPBRandomStream needs to be
      parallel-safe by default.  If multiple tasks interact with it in
      an uncoordinated fashion, this must be set to `true`.  If it will
      only be called from a single task, or if only one task will call
      into it at a time, setting to `false` will reduce overhead related
      to ensuring mutual exclusion.
      

   .. attribute:: const seed: int(64)

      
      The seed value for the PRNG.  It must be an odd integer in the
      interval [1, 2**46).
      

   .. method:: proc NPBRandomStream(seed: int(64) = SeedGenerator.oddCurrentTime, param parSafe: bool = true, type eltType = real(64))

      
      Constructs a new stream of random numbers using the specified seed
      and parallel safety.
      
      .. note::
      
        The NPB generator requires an odd seed value. Constructing
        an NPBRandomStream with an even seed value will cause a call to
        halt(). Only the lower 46 bits of the seed will be used.
      
      :arg seed: The seed to use for the PRNG.  Defaults to
        `oddCurrentTime` from :type:`~RandomSupport.SeedGenerator`.
      :type seed: `int(64)`
      
      :arg parSafe: The parallel safety setting.  Defaults to `true`.
      :type parSafe: `bool`
      
      :arg eltType: The element type to be generated.  Defaults to `real(64)`.
      :type eltType: `type`
      

   .. method:: proc getNext(): eltType

      
      Returns the next value in the random stream.
      
      Real numbers generated by the NPB RNG are in (0,1). It is not
      possible for this particular RNG to generate 0.0 or 1.0.
      
      :returns: The next value in the random stream as type :type:`eltType`.
      

   .. method:: proc skipToNth(n: integral)

      
      Advances/rewinds the stream to the `n`-th value in the sequence.
      
      :arg n: The position in the stream to skip to.  Must be > 0.
      :type n: `integral`
      

   .. method:: proc getNth(n: integral): eltType

      
      Advance/rewind the stream to the `n`-th value and return it
      (advancing the stream by one).  This is equivalent to
      :proc:`skipToNth()` followed by :proc:`getNext()`.
      
      :arg n: The position in the stream to skip to.  Must be > 0.
      :type n: `integral`
      
      :returns: The `n`-th value in the random stream as type :type:`eltType`.
      

   .. method:: proc fillRandom(arr: [] eltType)

      
      Fill the argument array with pseudorandom values.  This method is
      identical to the standalone :proc:`~Random.fillRandom` procedure,
      except that it consumes random values from the
      :class:`NPBRandomStream` object on which it's invoked rather
      than creating a new stream for the purpose of the call.
      
      :arg arr: The array to be filled
      :type arr: [] :type:`eltType`
      

   .. method:: proc iterate(D: domain, type resultType = real)

      
      
      Returns an iterable expression for generating `D.numIndices` random
      numbers. The RNG state will be immediately advanced by `D.numIndices`
      before the iterable expression yields any values.
      
      The returned iterable expression is useful in parallel contexts,
      including standalone and zippered iteration. The domain will determine
      the parallelization strategy.
      
      :arg D: a domain
      :arg resultType: the type of number to yield
      :return: an iterable expression yielding random `resultType` values
      
      

