# Source code for pypdevs.randomGenerator

    # Copyright 2014 Modelling, Simulation and Design Lab (MSDL) at
    # McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #    http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    """
    Module to offer 'really deterministic' (pseudo-)random number generation in a Distributed Time Warp implementation.

    For local simulation, using the random library from Python as usual is perfectly ok.
    """

    import random

    class RandomGenerator(object):
        """
        Base class, which implements a random number interface for the 'uniform' and 'random' Python standard library functions.

        .. note:: The generated random numbers are **not** equal to those generated by direct calls to the library functions, as we also use a random number to initialize the seed in the next iteration.
        """
        def __init__(self, seed):
            """
            Constructor

            :param seed: the seed to start with, this will simply be passed to the *random* library at every function call
            """

            #NOTE: This is implemented using only a seed (and actually, only a number), instead of using the 'getState()' en 'setState(state)'
            #      functions provided by the library. This was done to allow far more simple comparison (for memoization), hashing (as we
            #      have overwritten the comparison) and copying (for custom state saving).
            self.seed = seed

        def __eq__(self, other):
            """
            Compare two instances of random number generators.

            Needed for memoization.

            :param other: the instance to compare with
            :returns: bool -- do these random number generators return the same sequence?
            """
            return type(self) == type(other) and self.seed == other.seed

        def __hash__(self):
            """
            Hash this random number generator.

            Needed as the comparison method was changed!

            :returns: hash
            """
            return self.seed

        def copy(self):
            """
            A copy method to be used when defining custom state saving methods. It will return a complete copy of this random number
            generator, which will generate exactly the same sequence of numbers.
            """
            return RandomGenerator(self.seed)

        def __wrapFunction(self, func, args):
            """
            Internal wrapper for most functions, allows easy addition of new functions should the need arise. It updates the internal state and
            guarantees determinism even when revertions happen.

            :param func: the function to call on the *random* module (a string)
            :param args: the arguments to pass (a list)
            :returns: random -- the generated value
            """
            random.seed(self.seed)
            val = getattr(random, func)(*args)
            self.seed = random.random()
            return val

        def uniform(self, a, b):
            """
            Call the uniform function of the *random* library.

            :param a: lower bound of generated value
            :param b: upper bound of generated value
            :returns: float -- the generated value
            """
            return self.__wrapFunction("uniform", [a, b])

        def random(self):
            """
            Call the random function of the *random* library.

            :returns: float -- a random value between 0 and 1
            """
            return self.__wrapFunction("random", [])