## Advent Calendar - December 3, 2021

Friday, Dec 3, 2021| Tags: Perl, Raku

### | Day 2 | Day 3 | Day 4 |

The gift is presented by Arne Sommer. Today he is talking about his solution to “The Weekly Challenge - 097”. This is re-produced for Advent Calendar 2021 from the original post by `Arne Sommer`.

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`.

The expression `alphabets A..Z only` is wrong, as the example has several spaces as well. So they should be allowed.

File: caesar-cipher

``````#! /usr/bin/env raku

subset AZ-space of Str where /^ <[ A .. Z \s ]>+ \$/;   # [1]
subset PosInt of Int where -25 <= \$_ <= 25;            # [2]

unit sub MAIN (AZ-space \$S = 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG',
PosInt \$N = 3);                         # [3]

say \$S.comb.map({ caesar(\$_, \$N) }).join;              # [4]

sub caesar (\$char, \$shift)
{
return \$char if \$char eq " ";                        # [5]

my \$code = \$char.ord;                                # [6]

\$code -= \$shift;                                     # [7]

\$code += 26 if \$code < 65;  # 'A'                    # [8]
\$code -= 26 if \$code > 90;  # 'Z'                    # [8a]

return \$code.chr;                                    # [9]
}
``````

## Running it:

``````\$ ./caesar-cipher 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' 3
QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD

\$ ./caesar-cipher 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' -3
WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ

\$ ./caesar-cipher 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' 13
GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT

\$ ./caesar-cipher 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' -13
GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT
``````

Raku has a `ords` variant that takes a whole string, and not a single character as `ord`. And `chrs` which takes an array of codepoints and turns them into a string, and not a single codepoint to a character as chr. Let us use them to write a shorter program:

File: caesar-cipher-map

``````#! /usr/bin/env raku

subset AZ-space of Str where /^ <[ A .. Z \s ]>+ \$/;
subset PosInt of Int where -25 <= \$_ <= 25;

unit sub MAIN (AZ-space \$S = 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG',
PosInt \$N = 3);

say caesar(\$S, \$N);

sub caesar (\$string, \$shift)
{
return \$string.ords.map({\$_ == 32 ?? 32 !! ((\$_ - \$shift - 65) % 26 ) + 65}).chrs;
# #################### # 1a ############# ############ # 1b  # 1c ## 1d
}
``````

[1] We use `map` to change the individual codepoints. We let the spaces with codepoint 32 alone [1a]. Every other value we reduce to a number between 0 and 25 (by subtracting the codepoint of the first letter (A: 65) and the shift value [1b]. The modulo operator (`%`) takes care of negative values for us, doing the right thing. E.g. `-2 % 26 -> 24` [1c]. Then we add adjust the values up to where they should be (A to Z) [1d] before we turn the whole array of codepints into a string.

See docs.raku.org/routine/ords for more information about `ords`.

See docs.raku.org/routine/chrs for more information about `chrs`.

Running it gives the same result as before:

``````\$ ./caesar-cipher-map 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' 3
QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD

\$ ./caesar-cipher-map 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' -3
WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ

\$ ./caesar-cipher-map 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' 13
GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT

\$ ./caesar-cipher-map 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' -13
GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT
``````

## A Perl Version

This is straight forward translation of the first Raku version.

File: caesar-cipher-perl

``````#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';
use feature 'signatures';

no warnings "experimental::signatures";

my \$S = shift(@ARGV) // 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG';

die "Illegal characters" unless \$S =~ /^[A-Z\s]+\$/;

my \$N = shift(@ARGV) // 3;

die "Illegal shift \$N" if \$N !~ /^\-?\d+\$/ || \$N < -25 || \$N > 25;

say join("", map { caesar(\$_, \$N) } split(//, \$S));

sub caesar (\$char, \$shift)
{
return \$char if \$char eq " ";

my \$code = ord(\$char);

\$code -= \$shift;

\$code += 26 if \$code < 65;  # 'A'
\$code -= 26 if \$code > 90;  # 'Z'

return chr(\$code);
}
``````

Running it gives the same result as the Raku version:

``````\$ ./caesar-cipher-perl 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' 3
QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD

\$ ./caesar-cipher-perl 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' -3
WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ

\$ ./caesar-cipher-perl 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' 13
GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT

\$ ./caesar-cipher-perl 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG' -13
GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT
``````

If you have any suggestion then please do share with us perlweeklychallenge@yahoo.com.