#!/usr/bin/perl

# $sd = standard_deviation_data(\@array) computes the standard
# deviation of an array of numbers.
#
sub standard_deviation_data {
    my $arrayref = shift;
    my $mean = mean($arrayref);
    return sqrt( mean( [map $_ ** 2, @$arrayref] ) - ($mean ** 2) );
}

# @scores = standard_scores(\@array) computes the number
# of standard deviations above the mean for each element.
#
sub standard_scores {
    my $arrayref = shift;
    my $mean = mean($arrayref);
    my ($i, @scores);
    my $deviation = sqrt(mean( [map( ($_ - $mean) ** 2, @$arrayref)]));
    return unless $deviation;
    for ($i = 0; $i < @$arrayref; $i++) {
        push @scores, ($arrayref->[$i] - $mean) / $deviation;
    }
    return \@scores;
}

%results = (Arnold => 72, Barbara => 69, Charles => 68, Dominique => 80,
            Edgar => 85, Florentine => 84, Geraldo => 75, Hacker => 90,
            Inigo => 69, Jacqueline => 74, Klee => 83, Lissajous => 75,
            Murgatroyd => 77);

@values = values %results;
$mean   = mean(\@values);
$sd     = standard_deviation_data(\@values);
$scores = standard_scores(\@values);
print "The mean is $mean and the standard deviation is $sd.\n";

while (($name, $score) = each %results) {
    print "$name: ", " " x (10 - length($name)), grade($scores->[$i]);
    printf " (sd: %4.1f)\n", $scores->[$i];
    $i++;
}

sub grade {
    return "A" if $_[0] > 1.0;
    return "B" if $_[0] > 0.5;
    return "C" if $_[0] > -0.5;
    return "D" if $_[0] > -1.0;
    return "F";
}

# $mean = mean(\@array) computes the mean of an array of numbers.
#
sub mean {
    my ($arrayref) = @_;
    my $result;
    foreach (@$arrayref) { $result += $_ }
    return $result / @$arrayref;
}
