#!/usr/bin/perl

# Compares random number generation timings for Perl's core function,
# Math::Random::MT::Auto and Math::Random::MT (if available);

# Usage:  random [COUNT]

use strict;
use warnings;

$| = 1;

use Math::Random::MT::Auto qw/rand rand32 srand seed warnings gaussian :!auto/;
use Time::HiRes 'time';

MAIN:
{
    my $count = (@ARGV) ? $ARGV[0] : 624000;

    my ($cnt, $start, $end);

    print("Random numbers generation timing\n");

    # Time Perl's srand()
    print("\n- Core -\n");
    my $seed = time() + $$;
    $start = time();
    CORE::srand($seed);
    $end = time();
    printf("srand:\t\t%f secs.\n", $end - $start);

    # Time Perl's rand()
    $cnt = $count-1;
    $start = time();
    do {
        CORE::rand();
    } while ($cnt--);
    $end = time();
    printf("rand:\t\t%f secs. (%d)\n", $end - $start, $count);

    # Reseed
    CORE::srand($seed);

    # Time Perl's rand(arg)
    $cnt = $count-1;
    $start = time();
    do {
        CORE::rand(5);
    } while ($cnt--);
    $end = time();
    printf("rand(5):\t%f secs. (%d)\n", $end - $start, $count);


    my @seed = @{seed()};       # Copy of existing seed

    my @warnings = warnings(1); # Clear any existing error messages

    if ($^O eq 'MSWin32') {
        # Call srand to load Win32::API::Prototype
        srand('win32');
        # If errors, then ignore (probably not XP or no Win32::API::Prototype)
        @warnings = warnings(1);
        if (! @warnings) {
            # Time our srand() for win32
            $start = time();
            srand('win32');
            $end = time();
            printf("srand:\t\t%f secs. (Win32 XP)\n", $end - $start);
            @seed = @{seed()};
        }

    } else {
        # Time our srand() for /dev/random
        print("\n- Math::Random::MT::Auto - Standalone PRNG -\n");
        $start = time();
        srand('/dev/random');
        $end = time();
        # If errors, then ignore (probably no /dev/random)
        @warnings = warnings(1);
        if (! @warnings) {
            printf("srand:\t\t%f secs. (/dev/random)\n", $end - $start);
            @seed = @{seed()};
        }
    }

    # Call srand to load LWP::UserAgent
    @warnings = warnings(1);
    srand('random_org');
    # If errors, then ignore (probably no LWP::UserAgent)
    @warnings = warnings(1);
    if (! @warnings) {
        # Time our srand() for random.org
        $start = time();
        srand('random_org');
        $end = time();
        printf("srand:\t\t%f secs. (random.org)\n", $end - $start);
        @seed = @{seed()};
    }

    # Time our rand32()
    $cnt = $count-1;
    $start = time();
    do {
        rand32();
    } while ($cnt--);
    $end = time();
    printf("rand32:\t\t%f secs. (%d)\n", $end - $start, $count);

    # Reseed
    seed(\@seed);

    # Time our rand()
    $cnt = $count-1;
    $start = time();
    do {
        rand();
    } while ($cnt--);
    $end = time();
    printf("rand:\t\t%f secs. (%d)\n", $end - $start, $count);

    # Reseed
    seed(\@seed);

    # Time our rand(arg)
    $cnt = $count-1;
    $start = time();
    do {
        rand(5);
    } while ($cnt--);
    $end = time();
    printf("rand(5):\t%f secs. (%d)\n", $end - $start, $count);

    # Time gaussian()
    $cnt = $count-1;
    $start = time();
    do {
        gaussian();
    } while ($cnt--);
    $end = time();
    printf("gaussian:\t%f secs. (%d)\n", $end - $start, $count);

    # Time OO interface
    print("\n- Math::Random::MT::Auto - OO Interface -\n");
    my $rand;
    if ($^O eq 'MSWin32') {
        $start = time();
        $rand = Math::Random::MT::Auto->new('SOURCE' => ['win32']);
        $end = time();
        # If errors, then ignore (probably not XP or no Win32::API::Prototype)
        @warnings = $rand->warnings(1);
        if (! @warnings) {
            printf("new:\t\t%f secs. (Win32 XP)\n", $end - $start);
        }

    } else {
        $start = time();
        $rand = Math::Random::MT::Auto->new('SOURCE' => ['/dev/random']);
        $end = time();
        # If errors, then ignore (probably no /dev/random)
        @warnings = $rand->warnings(1);
        if (! @warnings) {
            printf("new:\t\t%f secs. (/dev/random)\n", $end - $start);
        }
    }

    # Time our srand() for random.org
    $start = time();
    $rand = Math::Random::MT::Auto->new('SOURCE' => ['random_org']);
    $end = time();
    # If errors, then ignore (probably no LWP::UserAgent)
    @warnings = $rand->warnings(1);
    if (! @warnings) {
        printf("new:\t\t%f secs. (random.org)\n", $end - $start);
    }

    # Reseed
    $rand->seed(\@seed);

    # Time our rand32()
    $cnt = $count-1;
    $start = time();
    do {
        $rand->rand32();
    } while ($cnt--);
    $end = time();
    printf("rand32:\t\t%f secs. (%d)\n", $end - $start, $count);

    # Reseed
    $rand->seed(\@seed);

    # Time our rand()
    $cnt = $count-1;
    $start = time();
    do {
        $rand->rand();
    } while ($cnt--);
    $end = time();
    printf("rand:\t\t%f secs. (%d)\n", $end - $start, $count);

    # Reseed
    $rand->seed(\@seed);

    # Time our rand(arg)
    $cnt = $count-1;
    $start = time();
    do {
        $rand->rand(5);
    } while ($cnt--);
    $end = time();
    printf("rand(5):\t%f secs. (%d)\n", $end - $start, $count);


    # See if Math::Random::MT is available
    eval { require Math::Random::MT; };
    if (! $@) {
        # Time its new(@seed) method
        print("\n- Math::Random::MT -\n");
        $start = time();
        $rand = Math::Random::MT->new(@seed);
        $end = time();
        printf("new:\t\t%f secs. (+ seed acquisition time)\n", $end - $start);

        # Time its rand() method
        $cnt = $count-1;
        $start = time();
        do {
            $rand->rand();
        } while ($cnt--);
        $end = time();
        printf("rand:\t\t%f secs. (%d)\n", $end - $start, $count);

        # Reseed
        $rand = Math::Random::MT->new(@seed);

        # Time its rand(arg) method
        $cnt = $count-1;
        $start = time();
        do {
            $rand->rand(5);
        } while ($cnt--);
        $end = time();
        printf("rand(5):\t%f secs. (%d)\n", $end - $start, $count);
    }
}

exit(0);

# EOF
