package TrigCalc;
use warnings;
use strict;
# use Data::Dump qw(dd pp); # for debug
use parent 'Exporter';
our $VERSION = '0.1.0';
our @EXPORT = qw(); # default exports: (none for now)
our @EXPORT_OK = # optional exports
qw(reduce)
;
use constant SECS_PER_DEGREE => 3600;
use constant SECS_PER_MINUTE => 60;
# exported subroutines #############################################
sub reduce {
# reduce from rightmost end of argument list.
return secs_2_dms(dms_2_secs((reverse @_)[ 0 .. 2 ]));
}
# non-exported (utility) subroutines ###############################
sub dms_2_secs {
my ($secs, # seconds
$mins, # minutes
$degs, # degrees
) = @_;
$secs ||= 0; # init as zero if undefined (or false: redundant)
$mins ||= 0;
$degs ||= 0;
return $degs * SECS_PER_DEGREE + $mins * SECS_PER_MINUTE + $secs;
}
sub secs_2_dms {
use integer;
my ($secs, # seconds
) = @_;
$secs ||= 0; # init as zero if undefined (or false: redundant)
my $degs = $secs / SECS_PER_DEGREE; $secs %= SECS_PER_DEGREE;
my $mins = $secs / SECS_PER_MINUTE; $secs %= SECS_PER_MINUTE;
return $degs, $mins, $secs;
}
1;
####
use warnings;
use strict;
use Test::More 'no_plan';
use Test::NoWarnings;
use Data::Dump qw(pp);
note "testing under perl version $]";
BEGIN { use_ok 'TrigCalc', qw(reduce); }
my @TESTS = (
'degenerate cases',
[ [], [ 0, 0, 0 ], 'empty argument list' ],
[ [ undef ], [ 0, 0, 0 ], 'undefined argument(s)' ],
[ [ undef, undef ], [ 0, 0, 0 ], 'undefined argument(s)' ],
[ [ undef, undef, undef ], [ 0, 0, 0 ], 'undefined argument(s)' ],
'irreducible cases',
[ [ 123, 180, 59, 59 ], [ 180, 59, 59 ], 'left ignored' ],
[ [ 180, 59, 59 ], [ 180, 59, 59 ], ],
[ [ 180, 59, 58 ], [ 180, 59, 58 ], ],
[ [ 1, 1, 1 ], [ 1, 1, 1 ], ],
[ [ 0, 0, 0 ], [ 0, 0, 0 ], ],
[ [ -1, -1, -1 ], [ -1, -1, -1 ], ],
[ [ -180, -59, -58 ], [ -180, -59, -58 ], ],
[ [ -123, -180, -59, -59 ], [ -180, -59, -59 ], 'left ignored' ],
'reducible cases',
[ [ 179, 0, 60 ], [ 179, 1, 0 ], ],
[ [ 179, 61, 61 ], [ 180, 2, 1 ], ],
[ [ 179, 59, 60 ], [ 180, 0, 0 ], ],
[ [ -179, 60, 0 ], [ -178, 0, 0 ], 'reduction of negatives' ],
# [ [ 0, 0, -60 ], [ -1, 59, 2 ], 'negative borrow' ], # <-- ????
[ [ 0, 0, -60 ], [ 0, -1, 0 ], 'negative borrow' ],
[ [ 90, 360, 360 ], [ 96, 6, 0 ], 'multiple reduction' ],
[ [ 90, -360, -360 ], [ 83, 54, 0 ], 'multiple reduction' ],
'only rightmost arguments are reduced, others ignored',
[ [ 200, 179, 0, 60 ], [ 179, 1, 0 ], ],
[ [ 200, 200, 179, 0, 60 ], [ 179, 1, 0 ], ],
[ [ 200, 200, 200, 179, 0, 60 ], [ 179, 1, 0 ], ],
[ [ 200, 90, -360, -360 ], [ 83, 54, 0 ], ],
[ [ 200, 200, 90, -360, -360 ], [ 83, 54, 0 ], ],
[ [ 200, 200, 200, 90, -360, -360 ], [ 83, 54, 0 ], ],
);
# just to be sure...
is_deeply [ reduce ], [ 0, 0, 0 ], 'REALLY empty argument list';
VECTOR:
for my $ar_vector (@TESTS) {
# comments can be mixed into test vector list.
if (not ref $ar_vector) {
note "--- $ar_vector ---"; # actually a comment
next VECTOR;
}
# expand test vector.
my ($ar_args, $ar_expected, $cmnt) = @$ar_vector;
# prepare fancy-shmancy test comment.
$cmnt = defined $cmnt ? ": $cmnt" : '';
my $args_str = pp @$ar_args;
my $expected_str = pp @$ar_expected;
my $full_comment = "$args_str -> $expected_str$cmnt";
# do it to it.
is_deeply [ reduce(@$ar_args) ], $ar_expected, $full_comment;
} # end for VECTOR
# a way to test non-exported functions.
note "\n=== testing TrigCalc::dms_2_secs() ===\n\n";
# aliasing not really necessary, but more sexy.
*dms2s = *TrigCalc::dms_2_secs;
VECTOR:
for my $ar_vector (
# only LEFT-most input args are converted.
# expected ------ input args ------
# seconds secs mins degs ignored
[ 0, ], # empty args list
[ 1, 1 ],
[ 62, 2, 1 ],
[ 3723, 3, 2, 1 ],
[ 7384, 4, 3, 2, 4 ],
[ 11045, 5, 4, 3, 5, 6 ],
[ 14706, 6, 5, 4, 7, 8, 9 ],
) {
if (not ref $ar_vector) {
note $ar_vector;
next VECTOR;
}
my ($expected, @args) = @$ar_vector;
is dms2s(@args), $expected, "(@args) == $expected seconds";
} # end for VECTOR
done_testing;
exit;
# support subroutines ##############################################
# none for now
##
##
c:\@Work\Perl\monks\thechartist>perl TrigCalc.t
ok 1 - use TrigCalc;
# testing under perl version 5.008009
ok 2 - REALLY empty argument list
# --- degenerate cases ---
ok 3 - () -> (0, 0, 0): empty argument list
ok 4 - undef -> (0, 0, 0): undefined argument(s)
ok 5 - (undef, undef) -> (0, 0, 0): undefined argument(s)
ok 6 - (undef, undef, undef) -> (0, 0, 0): undefined argument(s)
# --- irreducible cases ---
ok 7 - (123, 180, 59, 59) -> (180, 59, 59): left ignored
ok 8 - (180, 59, 59) -> (180, 59, 59)
ok 9 - (180, 59, 58) -> (180, 59, 58)
ok 10 - (1, 1, 1) -> (1, 1, 1)
ok 11 - (0, 0, 0) -> (0, 0, 0)
ok 12 - (-1, -1, -1) -> (-1, -1, -1)
ok 13 - (-180, -59, -58) -> (-180, -59, -58)
ok 14 - (-123, -180, -59, -59) -> (-180, -59, -59): left ignored
# --- reducible cases ---
ok 15 - (179, 0, 60) -> (179, 1, 0)
ok 16 - (179, 61, 61) -> (180, 2, 1)
ok 17 - (179, 59, 60) -> (180, 0, 0)
ok 18 - (-179, 60, 0) -> (-178, 0, 0): reduction of negatives
ok 19 - (0, 0, -60) -> (0, -1, 0): negative borrow
ok 20 - (90, 360, 360) -> (96, 6, 0): multiple reduction
ok 21 - (90, -360, -360) -> (83, 54, 0): multiple reduction
# --- only rightmost arguments are reduced, others ignored ---
ok 22 - (200, 179, 0, 60) -> (179, 1, 0)
ok 23 - (200, 200, 179, 0, 60) -> (179, 1, 0)
ok 24 - (200, 200, 200, 179, 0, 60) -> (179, 1, 0)
ok 25 - (200, 90, -360, -360) -> (83, 54, 0)
ok 26 - (200, 200, 90, -360, -360) -> (83, 54, 0)
ok 27 - (200, 200, 200, 90, -360, -360) -> (83, 54, 0)
#
# === testing TrigCalc::dms_2_secs() ===
#
ok 28 - () == 0 seconds
ok 29 - (1) == 1 seconds
ok 30 - (2 1) == 62 seconds
ok 31 - (3 2 1) == 3723 seconds
ok 32 - (4 3 2 4) == 7384 seconds
ok 33 - (5 4 3 5 6) == 11045 seconds
ok 34 - (6 5 4 7 8 9) == 14706 seconds
1..34
ok 35 - no warnings
1..35
##
##
sub reduce {
# reduce from rightmost end of argument list.
# left-pad input arguments with 0s to avoid inaccessible items.
my ($degs, # degrees
$mins, # minutes
$secs, # seconds
) = map $_ || 0, (0, 0, 0, @_)[ -3 .. -1 ]; # handles undefs
use integer;
$secs = $degs * SECS_PER_DEGREE + $mins * SECS_PER_MINUTE + $secs;
$degs = $secs / SECS_PER_DEGREE; $secs %= SECS_PER_DEGREE;
$mins = $secs / SECS_PER_MINUTE; $secs %= SECS_PER_MINUTE;
return $degs, $mins, $secs;
}