Acme-Selection-RarestFirst

 view release on metacpan or  search on metacpan

lib/Acme/Selection/RarestFirst.pm  view on Meta::CPAN

use v5.42;
use feature 'class';
no warnings 'experimental::class';
#
class Acme::Selection::RarestFirst v1.0.0 {
    use List::Util qw[shuffle];
    field $size : param;
    field @availability = (0) x $size;    # index => count

    #
    method update ( $bitfield, $delta ) {
        for ( my $i = 0; $i < $size; $i++ ) {
            if ( $bitfield->get($i) ) {
                $availability[$i] += $delta;
            }
        }
    }

    method pick ( $my_bitfield, $priorities //= () ) {
        my @candidates;
        for ( my $i = 0; $i < $size; $i++ ) {
            next if $my_bitfield->get($i);
            next if defined $priorities && ( $priorities->[$i] // 1 ) <= 0;
            push @candidates, $i;
        }
        return undef unless @candidates;

        # Sort by:
        # 1. User priority (higher first)
        # 2. Availability (lowest first)
        # 3. Random tie-break (achieved by shuffling before sort)
        @candidates = shuffle @candidates;
        @candidates = sort {
            my $p_a     = $priorities ? ( $priorities->[$a] // 1 ) : 1;
            my $p_b     = $priorities ? ( $priorities->[$b] // 1 ) : 1;
            my $avail_a = $availability[$a] // 0;
            my $avail_b = $availability[$b] // 0;
            ( $p_b <=> $p_a ) || ( $avail_a <=> $avail_b )
        } @candidates;
        return $candidates[0];
    }

    method get_availability ($index) {
        return $availability[$index] // 0;
    }
};
#
1;



( run in 0.398 second using v1.01-cache-2.11-cpan-d80b1682f3f )