#!/usr/bin/perl
#
# Perl QOTW #6
# entry by Shawn Poulson, 11/20/2002
# shawn.poulson@sap.com
#
# Overview:
# - format_number_list:  does only normalize numeric input, but does
#      not alter order or remove/change valid input values.  Invalid
#      values are tossed.
# - expand_number_list:  returns the identical (valid) input that was
#      originally given to format_number_list.
# - filter_number_list:  handles normalizing, remove dupes, and sort
#      ascending.
# - normalize_number: isInteger-ish functionality
#

@list = (1, 2, 4, 5, 6, 7, 9, 13, 24, 25, 26, 27);

print "     source list: " . join(", ", @list) . "\n\n";

print "calling filter_number_list...\n";
@list = filter_number_list(@list);
print "   filtered list: " . join(", ", @list) . "\n\n";

print "calling format_number_list...\n";
$str = format_number_list(@list);
print "formatted string: $str\n\n";

print "calling expand_number_list...\n";
@explist = expand_number_list($str);
print "   expanded list: " . join(", ", @explist) . "\n\n";

exit;


sub format_number_list {
   my @arr = ();
   my ($startnum,$lastnum,$dir) = undef;
   for my $num (grep {/./} normalize_number(@_)) {
      if ($dir) {
         if ($num-$dir != $lastnum) {
            push(@arr, "$startnum-$lastnum");
            $dir = 0;
         }
      }
      elsif (defined($lastnum)) {
         if (abs($lastnum-$num) == 1) {
            $startnum = $lastnum;
            $dir = $num-$lastnum;
         }
         else {
            push(@arr, $lastnum);
         }
      }
      $lastnum = int($num);
   }
   return(join(", ", @arr, $dir ? "$startnum-$lastnum" : $lastnum));
}

sub expand_number_list {
   my @arr = ();
   for (split(/\s*,\s*/, $_[0])) {
      my ($a,$b) = /(-?\d+)(?:\s*-\s*(-?\d+))?/;
      $b = defined($b) ? $b : $a;
      push(@arr, ($a <= $b) ? ($a..$b) : reverse($b..$a) );
   }
   return(@arr);
}

# filter out duplicates and non-numeric,
# normalize numeric to integer, sort ascending
sub filter_number_list {
   my %hash;
   @hash{normalize_number(@_)} = undef;
   return(sort {$a <=> $b} grep {/./} keys %hash);
}

# If scalar is a number, normalize it.  Otherwise, return undef.
sub normalize_number {
   my @arr = ();
   for (@_) {
      if (/^\s*-0+\s*$/) { # special case: -0 (technically NaN)
         push(@arr, 0);
         next;
      }
      my $match = /^\s*(-?\d+)(?:\.\d*)?\s*$/;
      push(@arr, $match ? int($1) : undef);
   }
   return(wantarray ? @arr : $arr[0]);
}

