#!/usr/bin/perl -w
#
# caesar v0.51
# This program attempts to decode Caesar cyphers.
# Copyright (C) 1999-2000, James Yolkowski (ajy@sentex.net).
# Originally written 11/16/1999.
# Minor fixes made 12/6/1999, 2/20/2000, 3/6/2000, 12/11/2000, 12/31/2000.
#

=head1 NAME

caesar - decrypt caesar cyphers

=head1 SYNOPSIS

caesar [rotation]

=head1 DESCRIPTION

The caesar utility attempts to decrypt caesar cyphers using
English letter frequency statistics.  caesar reads from the
standard input and writes to the standard output.

The optional argument rotation is a numerical value between
0 and 25.  If present, then that specific rotation value is
used.  This can be used to decode ROT-13 encoded messages.

=head1 BUGS

Letter frequency analysis can only do so much.  Small messages
or ones whose plaintext contains many "unusual" characters
might be decrypted incorrectly.

=head1 AUTHOR

James Yolkowski (ajy@sentex.net)

=head1 COPYRIGHT and LICENSE

This program is Copyright (C) 1999-2001, James Yolkowski.

This program is free and open software. You may use, modify, distribute
and sell this program (and any modified variants) in any way you wish,
provided you do not restrict others to do the same.

=cut

# %freq contains letter frequencies as taken from the caesar man page.
%freq = (E => 13, T => 10.5, A => 8.1, O => 7.9, N => 7.1, R => 6.8,
	 I => 6.3, S => 6.1, H => 5.2, D => 3.8, L => 3.4, F => 2.9,
	 C => 2.7, M => 2.5, U => 2.4, G => 2, P => 1.9, Y => 1.9,
	 W => 1.5, B => 1.4, V => .9, K => .4, X => .15, J => .13,
	 Q => .11, Z => .07);
@score = (0) x 26;

if (@ARGV) {
    # Obtain the rotation value specified on the command line.
    ($rot = shift) =~ /^\d+$/ or die <<'';
Syntax: caesar [rotation]
rotation is an integer between 0 and 25.

}

# If our input comes from a file, slurp it all in.
# If our input comes from the console, just read one line.
-t or undef $/;
$_ = <STDIN>;
defined or exit;

unless (defined $rot) {
    # Count the number of occurrences for each letter, and for
    # each of the 26 rotations, calculate a score which mostly
    # reflects the frequency of common letters in that rotation.
    # For example, award 13 points for each E, 10.5 for each T, etc.
    for $i ('A' .. 'Z') {
	$lcount = eval "y/\l$i$i//";
	for $j ('A' .. 'Z') {
	    $score[(ord($j) - ord($i)) % 26] += $lcount * $freq{$j};
	}
    }
    # Find the possibility which obtained the highest score.
    $rot = (sort {$score[$a] <=> $score[$b]} 0 .. 25)[-1];
}

# We've got our rotation value.  Rotate and print the result.
$newletters = substr((join '', 'A' .. 'Z') x 2, $rot % 26, 26);
eval "y/A-Za-z/$newletters\L$newletters/";
print;
