#!/usr/bin/perl use strict; use warnings; my $next = Gen_Iter( $ARGV[0] || 10 ); while ( defined( my $rand = $next->() ) ) { print "$rand\n"; } sub Gen_Iter { my $wrk = shift; # Create initial bitstring my $bytes = int ( $wrk / 8 ); my $pad = 8 - $wrk % 8; $pad += $pad == 8 ? -8 : 0; $bytes++ if $pad; my $str = "\0" x $bytes; vec($str, $_, 1) = 1 for $wrk .. $bytes * 8 - 1; # Establish initial boundaries my $beg = 0; my $end = $wrk; # return magic sub return sub { return () if ! $wrk--; my $num = Get_Random->($str, $beg, $end); $beg = Post_Zero( $str, $num ) if $num == $beg; $end = Prev_Zero( $str, $num ) if $num == $end; vec($str, $num, 1) = 1; return $num; }; } # Count leading 1s in a bitstring sub Leading { my $str = shift; $str =~ /^(\xFF*)/; my $skip = length $1; return -1 if $skip == length $str; my $byte = unpack("x${skip}A1", $str); my $extra = -1; while ( $extra++ < 6 ) { last if ! vec($byte, $extra, 1); } return $skip * 8 + $extra; } # Count trailing 1s in a bitstring sub Trailing { my $str = shift; my $len = length $str; $str =~ /(\xFF*)$/; my $match = length $1; return -1 if $len == $match; my $skip = $len - $match - 1; my $byte = unpack("x${skip}A1", $str); my $extra = 8; while ( $extra-- ) { last if ! vec($byte, $extra, 1); } return ($len - $skip - 1) * 8 + (7 - $extra); } # Find a 0 before current location (-1 if none) sub Prev_Zero { my ($str, $orig) = @_; my $cur = $orig; my $pos = $cur % 8; if ( $pos ) { while ( $pos-- ) { return $cur if ! vec($str, --$cur, 1); } } my $length = int($orig / 8); my $prev = substr($str, 0, $length); return -1 if ! $prev; my $gap = Trailing( $prev ); return -1 if $gap == -1; return $orig - (1 + $gap + $orig % 8); } # Find a 0 after current location (-1 if none) sub Post_Zero { my ($str, $orig) = @_; my $cur = $orig; my $pos = $cur % 8; if ( $pos < 7 ) { for ( $pos + 1 .. 7 ) { return $cur if ! vec($str, ++$cur, 1); } } my $byte = int($orig / 8); return -1 if $byte == length( $str ) - 1; my $tail = substr($str, $byte + 1); my $gap = Leading( $tail ); return -1 if $gap == -1; return $orig + $gap + ( 8 - $orig % 8 ); }; # Generate a random number within a specified range sub Gen_Random { return $_[0] + int( rand( $_[1] - $_[0] ) ); } # Select an available number randomly accounting for ghost sub Get_Random { my ($str, $beg, $end) = @_; my $guess = Gen_Random($beg, $end); my $dir = int rand 2; while ( $guess != -1 && vec($str, $guess, 1) ) { $guess = $dir ? Post_Zero( $str, $guess ) : Prev_Zero( $str, $guess ) ; } $guess = $dir ? $beg : $end if $guess == -1; return $guess; }