# Binary Substrings and Caesar ciphers: Perl Weekly Challenge 97

### TASK #1 › Caesar Cipher

Submitted by: Mohammad S Anwar

You are given string`$S`

containing alphabets A..Z only and a number`$N`

.Write a script to encrypt the given string

`$S`

using Caesar Cipher with left shift of size`$N`

.

Before we dive into this, I would like quote Bruce Schneier from the intro to his book, *Applied Cryptography*.

There are two kinds of cryptography in this world: cryptography that will stop your kid sister from reading your files, and cryptography that will stop major governments from reading your files.

That book is about the latter, but this algorithm is definitely the former. You might’ve caught that, seeing that it’s named after a man who turned the Roman Republic into the Roman Empire about 2000 years ago. There are web tools that can decipher your code in fractions of a second. But it’s fun to shovel bits around, isn’t it?

In short, we start by choosing a cipher. Here, we’re offsetting the alphabet by 3.

```
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
^ snip
XYZABCDEFGHIJKLMNOPQRSTUVW
```

We then change every `A`

in our plaintext to `X`

, and so on.

If we *just* wanted to do this, then we’ve recently seen that `tr///`

is a perfect operator for this job.

```
my $ciphertext = $plaintext = tr/A-Z/X-ZA-W/r;
```

There’s a problem with this, which is that `$N`

can change, but the `tr`

is set at compile time. I can’t do `tr/$plain/$cipher/`

, and I gave up on trying to make `eval "tr/$plain/$cipher/"`

work how I wanted.

I *could* do this, but it’s silly.

```
my $ciphertext;
$ciphertext = $plaintext = tr/A-Z/ZA-Y/r if $N == 1;
$ciphertext = $plaintext = tr/A-Z/Y-ZA-X/r if $N == 2;
$ciphertext = $plaintext = tr/A-Z/X-ZA-W/r if $N == 3;
$ciphertext = $plaintext = tr/A-Z/W-ZA-V/r if $N == 4;
...
```

So, let us assume we get to the point where we have `@plain`

being an array of our alphabet in order, and `@cipher`

being the letters they translate to. This is a way you might think to do it:

```
my $ciphertext = $plaintext ;
for my $i ( 0..$#plain ) {
$ciphertext =~ s/$plain[$i]/$cipher[$i]/gmx;
}
```

The problem here is that each letter is in both the plain and cipher list, so you have a great chance of moving from `A`

to `X`

to `U`

to a totally messed-up ciphertext.

#### There Has To Be A Better Way!

The transfer should be on each individual letter, so create a hash table such that `$hash->{$plain} = $cipher`

. So, to create the ciphertext, it’s as easy as:

```
my $t = join '',
map { $cipher{$_} ? $cipher{$_} : $_ }
split //, $s;
```

This way, each character of the plaintext is encyphered individually, so we can’t double-encrypt things accidentally.

#### The Code

```
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw{ say signatures state };
no warnings qw{ experimental };
use Getopt::Long;
my $n = 3;
my $s = 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG';
GetOptions(
'number=i' => \$n,
'string=s' => \$s,
);
caesar_cipher( $s, $n );
sub caesar_cipher ( $s, $n ) {
my @alpha = 'A' .. 'Z';
my @bet = @alpha;
for ( 1 .. $n ) {
unshift @bet, pop @bet;
}
my $alpha = join '', @alpha;
my $bet = join '', @bet;
my %cipher = map { $alpha[$_] => $bet[$_] } 0 .. $#alpha;
$s = uc $s;
my $t = join '', map { $cipher{$_} ? $cipher{$_} : $_ } split //, $s;
say <<"END";
INPUT:
\$S = "$s", \$N = $n
OUTPUT:
"$t"
Plain: $alpha
cipher: $bet
END
}
```

#### TRADECRAFT

```
QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD
```

`THE`

is a particularly comon word in English. We look at this and we might want to find the three-letter words, and `QEB`

shows up twice. Let’s assume that `QEB`

means `THE`

.

```
the NRFZH YOLTK CLU GRJMP LSBO the IXWV ALD
```

It’s a start. No other `Q`

, no other `E`

, but there is `B`

, and if we’re right, that’ll be an `E`

```
the NRFZH YOLTK CLU GRJMP LSeO the IXWV ALD
```

Generally, you would want to remove anything from the plaintext that can’t be encrypted and hidden, to avoid giving more hints than necessary.

```
QEBNRFZHYOLTKCLUGRJMPLSBOQEBIXWVALD
```

Granted, it means you should write your plaintext unambiguously. `THETARGETISNOWHERE`

is not what you would want to encrypt and send, either.

Although, you probably don’t want learn your tradecraft from a random blog, either.

### TASK #2 › Binary Substrings

Submitted by: Mohammad S Anwar You are given a binary string $B and an integer $S.

Write a script to split the binary string $B of size $S and then find the minimum number of flips required to make it all the same.

There’s a bitwise solution for this. Of this I am sure.

But this is Perl, and in Perl, we like strings. And last week, I used and blogged about exactly the thing that will tell me *exactly* the thing to tell me the number of flips.

The next part is pulling substrings the size of `$S`

…

```
my $sub = substr( $C, 0, $S );
```

and removing them from the string …

```
substr( $C, 0, $S ) = '';
```

and being thankful to remember that `substr`

works both as an **lvalue** and an **rvalue**.

In this case, we use it in a while loop, and the binary string `"101100101"`

becomes `"100101"`

, then `"101"`

, then `""`

, simply by removing the first `$S`

characters.

#### The Code

```
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw{ say signatures state };
no warnings qw{ experimental };
use List::Util qw{min};
binary_substrings( '101100101', 3 );
binary_substrings( '10110111', 4 );
binary_substrings( '111100000000', 4 );
sub binary_substrings ( $B, $S ) {
# we're comparing everything to the first substring in B
# of size S, so let's pull it out here.
# also copy B so it's unmodified when done.
my $base = substr $B, 0, $S;
my $C = $B;
my $total = 0;
my @list;
while ($C) {
my $sub = substr( $C, 0, $S );
substr( $C, 0, $S ) = '';
# I feel there MUST be a bitwise operator that
# would be perfect for this, but I never deal with
# bitwise operators, and we dealt with edit distance
# just last week
my $d = levenshtein_distance( $base, $sub );
$total += $d;
# d < 1 is a bit of belt-and-suspenders thinking,
# because coming from levenshtein, it will only be
# a non-negative integer, but still...
# and the examples distinguished between "1 flip"
# and "2 flips", so to handle singular and plural,
# we move to three cases.
if ( $d < 1 ) { push @list, qq{"$sub": 0 flip}; }
elsif ( $d == 1 ) {
push @list, qq{"$sub": 1 flip to make it "$base"};
}
else { push @list, qq{"$sub": $d flips to make it "$base"}; }
}
say qq{INPUT: \$B = "$B", \$S = $S};
say qq{Output: $total};
say join "\n\t", 'Binary Substrings:', @list;
say '';
}
# -------------------------------------------------------------------
# straight copy of Wikipedia's "Levenshtein Distance"
sub levenshtein_distance {
my ( $f, $g ) = @_;
my @a = split //, $f;
my @b = split //, $g;
# There is an extra row and column in the matrix. This is the
# distance from the empty string to a substring of the target.
my @d;
$d[$_][0] = $_ for ( 0 .. @a );
$d[0][$_] = $_ for ( 0 .. @b );
for my $i ( 1 .. @a ) {
for my $j ( 1 .. @b ) {
$d[$i][$j] = (
$a[ $i - 1 ] eq $b[ $j - 1 ]
? $d[ $i - 1 ][ $j - 1 ]
: 1 + min(
$d[ $i - 1 ][$j],
$d[$i][ $j - 1 ],
$d[ $i - 1 ][ $j - 1 ]
)
);
}
}
return $d[@a][@b];
}
```

```
INPUT: $B = "101100101", $S = 3Output: 1
Binary Substrings:
"101": 0 flip
"100": 1 flip to make it "101"
"101": 0 flip
INPUT: $B = "10110111", $S = 4
Output: 2
Binary Substrings:
"1011": 0 flip
"0111": 2 flips to make it "1011"
INPUT: $B = "111100000000", $S = 4
Output: 8
Binary Substrings:
"1111": 0 flip
"0000": 4 flips to make it "1111"
"0000": 4 flips to make it "1111"
```