#!/usr/bin/perl

sub min { # Numbers.
    my $min = shift;
    foreach ( @_ ) { $min = $_ if $_ < $min }
    return $min;
}

sub smin { # Strings.
    my $s_min = shift;
    foreach ( @_ ) { $s_min = $_ if $_ lt $s_min }
    return $smin;
}

sub gmin { # Generic.
    my $g_cmp = shift;
    my $g_min = shift;
    foreach ( @_ ) { $g_min = $_ if $g_cmp->( $_, $g_min ) < 0 }
    return $g_min;
}

sub max { # Numbers.
    my $max = shift;
    foreach ( @_ ) { $max = $_ if $_ > $max }
    return $max;
}

sub smax { # Strings.
    my $s_max = shift;
    foreach ( @_ ) { $s_max = $_ if $_ gt $s_max }
    return $s_max;
}

sub gmax { # Generic.
    my $g_cmp = shift;
    my $g_max = shift;
    foreach ( @_ ) { $g_max = $_ if $g_cmp->( $_, $g_max ) > 0 }
    return $g_max;
}

sub mini {
    my $l = $_[ 0 ];
    my $n = @{ $l };
    return ( ) unless $n;        # Bail out if no list is given.
    my $v_min = $l->[ 0 ];       # Initialize indices.
    my @i_min = ( 0 );

    for ( my $i = 1; $i < $n; $i++ ) {
        if ( $l->[ $i ] < $v_min ) {
            $v_min = $l->[ $i ]; # Update minimum and
            @i_min = ( $i );     # reset indices.
        } elsif ( $l->[ $i ] == $v_min ) {
            push @i_min, $i;     # Accumulate minimum indices.
        }
    }

    return @i_min;
}

sub maxi {
    my $l = $_[ 0 ];
    my $n = @{ $l };
    return ( ) unless $n;        # Bail out if no list is given.
    my $v_max = $l->[ 0 ];       # Initialize indices.
    my @i_max = ( 0 );

    for ( my $i = 1; $i < $n; $i++ ) {
        if ( $l->[ $i ] > $v_max ) {
            $v_max = $l->[ $i ]; # Update maximum and
            @i_max = ( $i );     # reset indices.
        } elsif ( $l->[ $i ] == $v_max ) {
            push @i_max, $i;     # Accumulate maximum indices.
        }
    }

    return @i_max;
}

sub gextri {
   my $g_cmp = $_[ 0 ];
   my $l     = $_[ 1 ];
   my $n     = @{ $l };
   return ( ) unless $n;                # Bail out if no list is given.
   my $v_min = $l->[ 0 ];
   my $v_max = $v_min;                  # The maximum so far.
   my @i_min = ( 0 );                   # The minima indices.
   my @i_max = ( 0 );                   # The maxima indices.
   my $v_cmp;                           # The result of comparison.

   for ( my $i = 1; $i < $n; $i++ ) {
       $v_cmp = $g_cmp->( $l->[ $i ], $v_min );
       if ( $v_cmp < 0 ) {
           $v_min = $l->[ $i ];         # Update minimum and reset minima.
           @i_min = ( $i );
       } elsif ( $v_cmp == 0 ) {
           push @i_min, $i ;            # Accumulate minima if needed.
       } else {                         # Not minimum: maybe maximum?
           $v_cmp = $g_cmp->( $l->[ $i ], $v_max );
           if ( $v_cmp > 0 ) {
               $v_max = $l->[ $i ];     # Update maximum and reset maxima.
               @i_max = ( $i );
           } elsif ( $v_cmp == 0 ) {
               push @i_max, $i;         # Accumulate maxima.
           }
        }                               # Else neither minimum nor maximum.
    }
    return ( \@i_min, \@i_max );
}

my @x = qw(31 41 59 26 59 26 35 89 35 89 79 32);

my @i_max = maxi(\@x);    # @i_max should now contain 7 and 9.
print "@i_max\n";

#        0  1  2  3  4  5  6  7  8  9 10 11
@x = qw(31 41 59 26 59 26 35 89 35 89 79 32);

my ($i_min, $i_max) = gextri(sub { $_[0] <=> $_[1] }, \@x);

# @$i_min now contains 3 and 5.
# @$i_max now contains 7 and 9.

print "i_min: @$i_min\n";
print "i_max: @$i_max\n";
