#! /usr/bin/perl -w
# Here is another way to do it, using a trigonometry 'toolkit'
# approach and a different interface. Sides are $a, $b, and $c, and
# corresponding opposing angles are $A, $B, and $C. Note that
# pathological cases will break the program (e.g., using two angles
# whose sum is greater than 180); more robust error checking is
# required.
use strict;
test();
main();
sub main {
print "Enter an angle (in degrees) and its opposing side:\n";
print 'Angle: ';
chomp(my $A = <STDIN>);
print 'Side : ';
chomp(my $a = <STDIN>);
print q/Enter 'a' if you have an angle, 's' if you have a side: /;
GET_WHAT:
chomp(my $what = lc <STDIN>);
if ($what eq 'a') {
print 'Other angle: ';
chomp(my $B = <STDIN>);
my $C = 180 - $A - $B;
my $b = aAB_to_b($a, $A, $B);
my $c = aAB_to_b($a, $A, $C);
printf 'The other two sides are %.2f and %.2f, '
. "and the other angle is %.2f\n",
$b, $c, $C;
}
elsif ($what eq 's') {
print 'Other side: ';
chomp(my $b = <STDIN>);
my $B = aAb_to_B($a, $A, $b);
my $C = 180 - $A - $B;
my $c = aAB_to_b($a, $A, $C);
printf 'The other side is %.2f, '
. "and the other two angles are %.2f and %.2f\n",
$c, $B, $C;
}
else {
print q/Please enter 'a' or 's': /;
goto GET_WHAT;
}
}
sub aAb_to_B {
my ($a, $A, $b) = @_;
return rad2deg(asin($b * sin(deg2rad($A)) / $a));
}
sub aAB_to_b {
my ($a, $A, $B) = @_;
return sin(deg2rad($B)) * $a / sin(deg2rad($A));
}
sub asin {
my $sine = shift;
return undef unless $sine >= -1 && $sine <= 1;
return atan2($sine, sqrt(1 - $sine * $sine));
}
use constant PI => 4 * atan2(1, 1);
sub deg2rad { $_[0] / 180 * PI }
sub rad2deg { $_[0] / PI * 180 }
sub test {
# More tests would be welcome.
expect('aAB_to_b', 10, 5, 30, 90);
expect('aAb_to_B', 90, 5, 30, 10);
}
sub expect {
my ($sub, $exp, @args) = @_;
no strict 'refs';
my $got = $sub->(@args);
return if abs($got - $exp) < 0.0001;
local $" = ', ';
printf "Expected $sub(@args) to give $exp, but got %.2f\n", $got;
}