#! /usr/bin/perl use CGI::Carp qw(fatalsToBrowser warningsToBrowser); use POSIX qw(ceil); use strict; use warnings; print "Content-Type: text/plain\n\n"; warningsToBrowser(1); ########################################################### my $weight = 800; my %postage = Postage($weight); while (my ($location, $cost_ref) = each %postage) { print "$location\n"; print "\t@$_\n" for @$cost_ref; } ########################################################### our %POSTAGE; INIT { local $/ = "\n\n"; # Paragraph mode while () { chomp; # Comments begin with # my ($service, @rates) = grep {!/^#/} split "\n"; # Eval rates for (@rates) { $_ = [split "\t"]; $_->[1] = eval $_->[1]; die "$service - $@" if $@; } $POSTAGE{$service} = \@rates; } } sub Postage { my $weight = shift; die "Invalid weight" unless $weight < 1_000_000_000; my %postage; for my $service (sort keys %POSTAGE) { # The Last Max my $max = $POSTAGE{$service}[-1][0]; # Subdivide Package according to max weight my @weights = $weight; while ($max && $weights[-1] > $max) { unshift @weights, $max; $weights[-1] -= $max; } for my $w (@weights) { for my $rate (@{$POSTAGE{$service}}) { next if $rate->[0] && $w > $rate->[0]; push @{$postage{$service}}, [$w, sprintf "%.2f", $rate->[1]($w)]; last; } } } return %postage; } ########################################################### # Each section is __DATA__ is labelled by a destination # and then by max weight and the sub that determines the cost. # If no max weight is specified, then treat as infinate. __DATA__ UK & Europe sub { 0.00 } USA 200 sub { 2.00 } sub { my $w = shift; 2.00 + 1.00 * ceil(($w-200)/100) } Australia & New Zealand 200 sub { 2.00 } 2200 sub { my $w = shift; 2.00 * (1 + 1 / 1.1) ** ceil(($w - 200) / 200) } Other 200 sub { 2.00 } sub { my $w = shift; 2.00 + 1.00 * ceil(($w - 200) / 200) } Test 100 sub { 1.00 } 200 sub { 1.00 + 0.99 } 300 sub { 1.00 + 0.99 + 0.80 }