#!/usr/bin/perl -w

use strict;

my $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
my $show_stats = 1;

sub decode
  {
  my $l_i;
  my $l_j;
  my $l_rd;
  my $l_groups;		# Number of text groups
  my $l_shift;		# Offset into the alphabet (stage II decryption)
  my $l_stepping;	# Step width for the single iterations
  my $l_framewidth;	# Frame width
  my $l_inmsg;		# Buffer for ciphertext
  my $l_outmsg;		# Buffer for cleartext
  my $l_index;		# Index into cleartext buffer
  my $l_offset;		# Offset into ciphertext buffer
  my $l_textlen;	# Length of input stream

  $l_inmsg = $_[0];
  $l_outmsg = '';
  $l_textlen = length($l_inmsg);
  for($l_i = 0; $l_i < $l_textlen; $l_i++)
    {
    $l_outmsg .= ' ';
    }

# Stage I decode: Unwind the ciphertext
  $l_rd = $_[1];
  $l_shift = $_[2];
  $l_stepping = 2 ** ($l_rd - 2) | 1;
  $l_framewidth = 2 ** $l_rd;
  $l_groups = int((length($l_inmsg) + 2 ** $l_rd - 1) / (2 ** $l_rd));
  $l_offset = 0;

  if($show_stats == 1)
    {
    print "Initial data:\n";
    print "Ciphertext length: $l_textlen\n";
    print "Number of frames: $l_groups\n";
    print "Frame width: $l_framewidth\n";
    print "Width of last frame: ",$l_textlen - $l_framewidth * ($l_groups - 1),"\n";
    print "Stepping between iterations: $l_stepping\n";
    print "Offset into translation table: $l_shift\n";
    print "=================================================\n";
    }

  for($l_i = 0; $l_i < $l_framewidth / 2; $l_i++)
    {
    my $l_pass;

    if($l_rd % 2 == 0)
      {
      $l_index = ($l_i * $l_stepping) % $l_framewidth;
      }
    else
      {
      $l_index = ($l_i * ($l_framewidth - $l_stepping)) % $l_framewidth;
      }
    $l_pass = $l_i * 2;
    for($l_j = 0; $l_j < $l_groups; $l_j++)
      {
      if($l_index < length($l_inmsg))
        {
        substr($l_outmsg,$l_index,1,substr($l_inmsg,$l_offset,1));
        $l_offset++;
        }
      $l_index += $l_framewidth;
      }
    if($l_rd % 2 == 0)
      {
      $l_index = ($l_groups * $l_framewidth) - ((($l_i + 1) * $l_stepping) % $l_framewidth);
      }
    else
      {
      $l_index = ($l_groups * $l_framewidth) - $l_framewidth + ((($l_i + 1) * $l_stepping) % $l_framewidth);
      }
    $l_pass = $l_i * 2 + 1;
    for($l_j = 0; $l_j < $l_groups; $l_j++)
      {
      if($l_index < length($l_inmsg))
        {
        substr($l_outmsg,$l_index,1,substr($l_inmsg,$l_offset,1));
        $l_offset++;
        }
      $l_index -= $l_framewidth;
      }
    }

# Stage II decode: Transpose the alphabet where necessary
  if($l_shift > 0)
    {
    for($l_i = 0; $l_i < length($l_outmsg); $l_i++)
      {
      substr($l_outmsg,$l_i,1,substr($alphabet,(ord(substr($l_outmsg,$l_i,1)) - 39 - $l_shift) % 26,1)) if(index($alphabet,substr($l_outmsg,$l_i,1)) != -1);
      }
    }

  $l_outmsg =~ tr/-/ /;

  return $l_outmsg;
  }

my $ciphertext;
my $cipherid;
my $cleartext;

my $m;
my $n;

print 'Please provide the ciphertext: ';
$ciphertext = <STDIN>;
chop($ciphertext);
print 'Please provide the key: ';
$cipherid = <STDIN>;
chop($cipherid);

print "\n\n";

if($cipherid =~ /^!#RD([0-9]{1,})SB([0-9]{1,})\/\/$/)
  {
  if($1 <= 0 || $2 < 0 || $2 > 25)
    {
    print "Invalid encryption key!\n";
    print "m must be a nonzero positive integer, and n must be in the range\nfrom 0 to 25!";
    }
  else
    {
    $cleartext = decode($ciphertext,$1,$2);
    print "Cleartext: $cleartext\n";
    }
  }
else
  {
  print "That\'s not a valid key!\nThe key must be in the form\n";
  print "!#RD<m>SB<n>//\n";
  print "m and n are positive integers.\n";
  }
