The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package List::Util::WeightedChoice;

use 5.006000;
use strict;
use warnings;

use Carp qw(croak);
require Exporter;
use AutoLoader qw(AUTOLOAD);
use Params::Validate qw(:all);


our @ISA = qw(Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration	use List::Util::WeightedChoice ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
   choose_weighted
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(
	
);

our $VERSION = '0.06';



sub choose_weighted{
     validate_pos(@_, 
		  { type => ARRAYREF },
		  { type => CODEREF | ARRAYREF}
 	);

    my ($objects, $weightsArg ) = @_;
    my $calcWeight = $weightsArg if 'CODE' eq ref $weightsArg;
    my @weights;		# fix wasteful of memory
    if( $calcWeight){
	@weights =  map { $calcWeight->($_) } @$objects; 
    }
    else{
	@weights =@$weightsArg;
	if ( @$objects != @weights ){
	    croak "given arefs of unequal lengths!";
	}
    }

    my @ranges = ();		# actually upper bounds on ranges
    my $left = 0;
    for my $weight( @weights){
	$weight = 0 if $weight < 0; # the world is hostile...
	my $right = $left+$weight;
	push @ranges, $right;
	$left = $right;
    }
    my $weightIndex = rand $left;
    for( my $i =0; $i< @$objects; $i++){
	my $range = $ranges[$i];
	return $objects->[$i] if $weightIndex < $range;
    }
}


# Preloaded methods go here.

# Autoload methods go after =cut, and are processed by the autosplit program.

1;
__END__
# Below is stub documentation for your module. You'd better edit it!

=head1 NAME

List::Util::WeightedChoice - Perl extension to allow for nonnormalized weighted choices

=head1 SYNOPSIS

  use List::Util::WeightedChoice qw( choose_weighted );
  my $choices = ['Popular', 'Not so much', 'Unpopular'];
  my $weights = [ 50, 25, 1] ;
  my $choice = choose_weighted( $choices, $weights );


  my $complexChoices = [ 
    {val=>"Not so much", weight=>2},
    {val=>"Popular", weight=>50},
    {val=>"Unpopular", weight=>1},
    ];

  $choice = choose_weighted($complexChoices, sub{ $_[0]->{weight} } );


=head1 DESCRIPTION

Just one function, a simple means of making a weighted random choice

The implementation uses rand to calculate random numbers.

=head2 EXPORT

None by default.


=head3 choose_weighted

 choose_weighted ($object_Aref, $weights_Aref )

or 
 choose_weighted ($object_Aref, $weights_codeRef )

In the second case, the coderef is called on each object to determine its weight;

=head1 SEE ALSO

List::Util

=head1 CAVEATS


TODO: object-oriented module to implement fast re-picks with binary searches.

OO-interface ought to allow for other sources of randomness;
 
This currently does a linear search to find the winner.  It could be made faster

=head1 AUTHOR

Danny Sadinoff, <lt>danny-cpan@sadinoff.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2009 by Danny Sadinoff

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.


=cut