Math::Random::ISAAC::PP - Pure Perl port of the ISAAC PRNG algorithm


Math-Random-ISAAC documentation Contained in the Math-Random-ISAAC distribution.

Index


Code Index:

NAME

Top

Math::Random::ISAAC::PP - Pure Perl port of the ISAAC PRNG algorithm

VERSION

Top

version 1.004

SYNOPSIS

Top

This module implements the same interface as Math::Random::ISAAC and can be used as a drop-in replacement. However, it is recommended that you let the Math::Random::ISAAC module decide whether to use the PurePerl or XS version of this module, instead of choosing manually.

Selecting the backend to use manually really only has two uses:

Example code:

  # With Math::Random::ISAAC
  my $rng = Math::Random::ISAAC->new(time);
  my $rand = $rng->rand();

  # With Math::Random::ISAAC::PP
  my $rng = Math::Random::ISAAC::PP->new(time);
  my $rand = $rng->rand();

DESCRIPTION

Top

See Math::Random::ISAAC for the full description.

METHODS

Top

new

  Math::Random::ISAAC::PP->new( @seeds )

Implements the interface as specified in Math::Random::ISAAC

rand

  $rng->rand()

Implements the interface as specified in Math::Random::ISAAC

irand

  $rng->irand()

Implements the interface as specified in Math::Random::ISAAC

SEE ALSO

Top

Math::Random::ISAAC

BUGS

Top

Please report any bugs or feature requests on the bugtracker website http://rt.cpan.org/NoAuth/Bugs.html?Dist=Math-Random-ISAAC

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

AUTHOR

Top

Jonathan Yu <jawnsy@cpan.org>

COPYRIGHT AND LICENSE

Top


Math-Random-ISAAC documentation Contained in the Math-Random-ISAAC distribution.

package Math::Random::ISAAC::PP;
BEGIN {
  $Math::Random::ISAAC::PP::VERSION = '1.004';
}
# ABSTRACT: Pure Perl port of the ISAAC PRNG algorithm

use strict;
use warnings;
use Carp ();


sub new {
  my ($class, @seed) = @_;

  my $seedsize = scalar(@seed);

  my @mm;
  $#mm = $#seed = 255; # predeclare arrays with 256 slots

  # Zero-fill our seed data
  for ($seedsize .. 255) {
    $seed[$_] = 0;
  }

  my $self = {
    randrsl   => \@seed,
    randcnt   => 0,
    randmem   => \@mm,

    randa     => 0,
    randb     => 0,
    randc     => 0,
  };

  bless($self, $class);

  $self->_randinit();

  return $self;
}


# This package should have an interface similar to the builtin Perl
# random number routines; these are methods, not functions, so they
# are not problematic
## no critic (ProhibitBuiltinHomonyms)

sub rand {
  my ($self) = @_;

  return ($self->irand() / (2**32-1));
}


sub irand {
  my ($self) = @_;

  # Reset the sequence if we run out of random stuff
  if (!$self->{randcnt}--)
  {
    # Call method like this because of our hack above
    _isaac($self);
    $self->{randcnt} = 255;
  }

  return sprintf('%u', $self->{randrsl}->[$self->{randcnt}]);
}

# C-style for loops are used a lot since this is a port of the C version
## no critic (ProhibitCStyleForLoops)

# Numbers are specified in hex, so they don't need separators
## no critic (RequireNumberSeparators)

sub _isaac {
  my ($self) = @_;

  # Use integer math
  use integer;

  my $mm = $self->{randmem};
  my $r = $self->{randrsl};

  # $a and $b are reserved (see 'sort')
  my $aa = $self->{randa};
  my $bb = ($self->{randb} + (++$self->{randc})) & 0xffffffff;

  my ($x, $y); # temporary storage

  # The C code deals with two halves of the randmem separately; we deal with
  # it here in one loop, by adding the &0xff parts. These calls represent the
  # rngstep() macro, but it's inlined here for speed.
  for (my $i = 0; $i < 256; $i += 4)
  {
    $x = $mm->[$i  ];
    $aa = (($aa ^ ($aa << 13)) + $mm->[($i   + 128) & 0xff]);
    $aa &= 0xffffffff; # Mask out high bits for 64-bit systems
    $mm->[$i  ] = $y = ($mm->[($x >> 2) & 0xff] + $aa + $bb) & 0xffffffff;
    $r->[$i  ] = $bb = ($mm->[($y >> 10) & 0xff] + $x) & 0xffffffff;

    # I don't actually know why the "0x03ffffff" stuff is for. It was in
    # John L. Allen's code. If you can explain this please file a bug report.
    $x = $mm->[$i+1];
    $aa = (($aa ^ (0x03ffffff & ($aa >> 6))) + $mm->[($i+1+128) & 0xff]);
    $aa &= 0xffffffff;
    $mm->[$i+1] = $y = ($mm->[($x >> 2) & 0xff] + $aa + $bb) & 0xffffffff;
    $r->[$i+1] = $bb = ($mm->[($y >> 10) & 0xff] + $x) & 0xffffffff;

    $x = $mm->[$i+2];
    $aa = (($aa ^ ($aa << 2)) + $mm->[($i+2 + 128) & 0xff]);
    $aa &= 0xffffffff;
    $mm->[$i+2] = $y = ($mm->[($x >> 2) & 0xff] + $aa + $bb) & 0xffffffff;
    $r->[$i+2] = $bb = ($mm->[($y >> 10) & 0xff] + $x) & 0xffffffff;

    $x = $mm->[$i+3];
    $aa = (($aa ^ (0x0000ffff & ($aa >> 16))) + $mm->[($i+3 + 128) & 0xff]);
    $aa &= 0xffffffff;
    $mm->[$i+3] = $y = ($mm->[($x >> 2) & 0xff] + $aa + $bb) & 0xffffffff;
    $r->[$i+3] = $bb = ($mm->[($y >> 10) & 0xff] + $x) & 0xffffffff;
  }

  $self->{randb} = $bb;
  $self->{randa} = $aa;

  return;
}

sub _randinit
{
  my ($self) = @_;

  use integer;

  # $a and $b are reserved (see 'sort'); $i is the iterator
  my ($c, $d, $e, $f, $g, $h, $j, $k);
  $c=$d=$e=$f=$g=$h=$j=$k = 0x9e3779b9; # The golden ratio

  my $mm = $self->{randmem};
  my $r = $self->{randrsl};

  for (1..4)
  {
    $c ^= $d << 11;
    $f += $c;
    $d += $e;

    $d ^= 0x3fffffff & ($e >> 2);
    $g += $d;
    $e += $f;

    $e ^= $f << 8;
    $h += $e;
    $f += $g;

    $f ^= 0x0000ffff & ($g >> 16);
    $j += $f;
    $g += $h;

    $g ^= $h << 10;
    $k += $g;
    $h += $j;

    $h ^= 0x0fffffff & ($j >> 4);
    $c += $h;
    $j += $k;

    $j ^= $k << 8;
    $d += $j;
    $k += $c;

    $k ^= 0x007fffff & ($c >> 9);
    $e += $k;
    $c += $d;
  }

  for (my $i = 0; $i < 256; $i += 8)
  {
    $c += $r->[$i  ];
    $d += $r->[$i+1];
    $e += $r->[$i+2];
    $f += $r->[$i+3];
    $g += $r->[$i+4];
    $h += $r->[$i+5];
    $j += $r->[$i+6];
    $k += $r->[$i+7];

    $c ^= $d << 11;
    $f += $c;
    $d += $e;

    $d ^= 0x3fffffff & ($e >> 2);
    $g += $d;
    $e += $f;

    $e ^= $f << 8;
    $h += $e;
    $f += $g;

    $f ^= 0x0000ffff & ($g >> 16);
    $j += $f;
    $g += $h;

    $g ^= $h << 10;
    $k += $g;
    $h += $j;

    $h ^= 0x0fffffff & ($j >> 4);
    $c += $h;
    $j += $k;

    $j ^= $k << 8;
    $d += $j;
    $k += $c;

    $k ^= 0x007fffff & ($c >> 9);
    $e += $k;
    $c += $d;

    $mm->[$i  ] = $c;
    $mm->[$i+1] = $d;
    $mm->[$i+2] = $e;
    $mm->[$i+3] = $f;
    $mm->[$i+4] = $g;
    $mm->[$i+5] = $h;
    $mm->[$i+6] = $j;
    $mm->[$i+7] = $k;
  }

  for (my $i = 0; $i < 256; $i += 8)
  {
    $c += $mm->[$i  ];
    $d += $mm->[$i+1];
    $e += $mm->[$i+2];
    $f += $mm->[$i+3];
    $g += $mm->[$i+4];
    $h += $mm->[$i+5];
    $j += $mm->[$i+6];
    $k += $mm->[$i+7];

    $c ^= $d << 11;
    $f += $c;
    $d += $e;

    $d ^= 0x3fffffff & ($e >> 2);
    $g += $d;
    $e += $f;

    $e ^= $f << 8;
    $h += $e;
    $f += $g;

    $f ^= 0x0000ffff & ($g >> 16);
    $j += $f;
    $g += $h;

    $g ^= $h << 10;
    $k += $g;
    $h += $j;

    $h ^= 0x0fffffff & ($j >> 4);
    $c += $h;
    $j += $k;

    $j ^= $k << 8;
    $d += $j;
    $k += $c;

    $k ^= 0x007fffff & ($c >> 9);
    $e += $k;
    $c += $d;

    $mm->[$i  ] = $c;
    $mm->[$i+1] = $d;
    $mm->[$i+2] = $e;
    $mm->[$i+3] = $f;
    $mm->[$i+4] = $g;
    $mm->[$i+5] = $h;
    $mm->[$i+6] = $j;
    $mm->[$i+7] = $k;
  }

  $self->_isaac();
  $self->{randcnt} = 256;

  return;
}


1;

__END__