#!/usr/bin/perl

use strict;
use Getopt::Std;

my($p_name)   = $0 =~ m|/?([^/]+)$|;
my $p_version = "20030319.2";
my $p_usage   = "Usage: $p_name [--help] [-if] [-p<precision>] [-s<seed>] "
              . "[-n<count>] [<max>|<min>-<max>]";
my $p_cp      = <<EOM;
Copyright (c) 2003
      John Jetmore <jj33\@pobox.com>.  All rights reserved.
This code freely redistributable provided my name and this copyright notice
are not removed.  Send email to the contact address if you use this program.
EOM
ext_usage();

my %opt   = ();
getopts('s:n:fp:i', \%opt) || mexit(2);
# s - seed for srand
# n - number of random numbers to produce in one run
# f - produce floats instead of ints
# p - specify precision - implies -f
# i - increment by one on <high> calls (rand -i 6 is 1-6, not 0-5)

my $range = shift || "1-100";
my $high  = 0;
my $low   = 0;
my $seed  = $opt{s} || (time() ^ ($$ + ($$ << 15)));
my $count = 1;

if ($opt{n}) {
  if ($opt{n} > 0 && $opt{n} =~ /^\d+$/) {
    $count = $opt{n};
  } else {
    mexit(3, "options to -n must be an integer > 0");
  }
}
if ($opt{p}) {
  if ($opt{p} <= 0 || $opt{p} !~ /^\d+$/) {
    mexit(4, "options to -p must be an integer > 0");
  }
  $opt{p} = '.' . $opt{p};
}

if ($range =~ /^(-?\d+)-(-?\d+)$/) {
  $low  = $1;
  $high = $2;
} elsif ($range =~ /^\d+$/) {
  if ($opt{i}) {
    $low  = 1;
    $high = $range;
  } else {
    $low  = 0;
    $high = $range - 1;
  }
} else {
  mexit(1);
}

srand($seed);
foreach (1..$count) {
  my $r = rand($high + 1 - $low) + $low;
  if ($opt{f} || $opt{p}) {
    $r = sprintf("%$opt{p}f", $r);
  } else {
    $r = int($r);
  }
  print $r, "\n";
  #print int(rand($high + 1 - $low)) + $low, "\n";
}

exit;

sub mexit {
  my $exit = shift || 11;
  my $msg  = shift || $p_usage;

  print STDERR "$msg\n";
  exit($exit);
}

sub ext_usage {
  if ($ARGV[0] =~ /^--help$/i) {
    require Config;
    $ENV{PATH} .= ":" unless $ENV{PATH} eq "";
    $ENV{PATH} = "$ENV{PATH}$Config::Config{'installscript'}";
    exec("perldoc", "-F", "-U", $0) || exit 1;
    # make parser happy
    %Config::Config = ();
  } elsif ($ARGV[0] =~ /^--version$/i) {
    print "$p_name version $p_version\n\n$p_cp\n";
  } else {
    return;
  }

  exit(0);
}

__END__

=head1 NAME

rand - Produce a random number

=head1 USAGE

rand [--help|--version] | [-if] [-p<precision>] [-s<seed>] [-n<count>] [<max>|<min>-<max>]

=head1 OPTIONS

=over 4

=item -i

when specifying only <max>, x will be in 1 <= x <= <max>, instead of
0 <= x < <max>.

=item -f

numbers are provided as floats, not as integers.

=item -p

specify precision (implies -f).

=item -s

specify the seed to the random number generator - produce predictable
number sequences.

=item -n

specify an amount of random numbers to be generated, one per line.

=item --help

This screen.

=item --version

version info.

=back

=head1 EXAMPLES

=over 4

=item rand 6

produces integer x such that 0 <= x < 6

=item rand

behaves like 'rand 100', 0 <= x < 100

=item rand 1-6

1 <= x <= 6

=item rand -10-10

-10 <= x <= 10

=item rand -n 2 10-20

produces 2 numbers, each 10 <= x <= 20

=back

=head1 COMMENTS

calling rand with just <max> mimics perl's rand(), with
the exception of returning an integer.  However, since this
isn't what most people expect, specifying a range is
inclusive on both ends.
To clarify, 'rand 6' returns a value in the set [012345],
while 'rand 0-6' returns a value in the set [0123456].

=head1 EXIT CODES

=item 0 - no errors occurred

=item 1 - improper target range

=item 2 - error parsing command line arguments

=item 3 - error in -n

=item 4 - error in -p


=head1 CONTACT

=item proj-rand@jetmore.net

