in reply to math mistake?

sulfericacid is right, there's no reason to use negative indices to do this; you start counting page numbers from the beginning of the array anyway, and it's not too tough to calculate the start and end indices of the range. Here's some pagination code I use in my photo album software, which correctly handles all edge cases I could think of at the time I wrote it:
use POSIX 'ceil'; my @things = 1 .. 42; my $per_page = 20; my $num_pages = ceil( @things / $per_page ); my $page = param('page') || 1; ## verify page num is within range $page = 1 if $page < 1; $page = $num_pages if $page > $num_pages; my $first = ($page-1) * $per_page; my $last = $first + $per_page - 1; $last = $#things if $last > $#things; print @things[ $first .. $last ];
I also at least 2 odd things about this line:
for ( grep defined($_), ( reverse keys %upload )[ $top .. $bottom ] ) +{
1: You're using the keys of a hash to paginate. This is a very bad idea. When you add something to the hash, the ordering of keys %upload can change. To make things worse, in the newest (and all future) versions of Perl, the ordering of keys will completely change between program instances, even if the data hasn't changed. This would make your pagination useless, as you would basically get a random page of 20 things each time, no matter what page number you give to your CGI script. Use an array of things, or else sort the hash keys to make the ordering consistent between datasets and program instances.

2: Why do you have grep defined($_) ?? Are you expecting an undef hash key? Did you really mean grep exists $upload{$_} instead?

HTH

blokhead