From 5705fb90fdd5c035897c0f353f2a0e25622a86ac Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Mon, 18 Nov 2024 10:10:23 -0500 Subject: [PATCH] Add rotational-cipher exercise (#690) --- config.json | 8 +++ .../rotational-cipher/.docs/instructions.md | 29 ++++++++ .../rotational-cipher/.meta/config.json | 22 ++++++ .../.meta/solutions/lib/RotationalCipher.pm | 25 +++++++ .../.meta/solutions/t/rotational-cipher.t | 1 + .../.meta/template-data.yaml | 33 +++++++++ .../rotational-cipher/.meta/tests.toml | 40 +++++++++++ .../rotational-cipher/lib/RotationalCipher.pm | 12 ++++ .../rotational-cipher/t/rotational-cipher.t | 69 +++++++++++++++++++ 9 files changed, 239 insertions(+) create mode 100644 exercises/practice/rotational-cipher/.docs/instructions.md create mode 100644 exercises/practice/rotational-cipher/.meta/config.json create mode 100644 exercises/practice/rotational-cipher/.meta/solutions/lib/RotationalCipher.pm create mode 120000 exercises/practice/rotational-cipher/.meta/solutions/t/rotational-cipher.t create mode 100644 exercises/practice/rotational-cipher/.meta/template-data.yaml create mode 100644 exercises/practice/rotational-cipher/.meta/tests.toml create mode 100644 exercises/practice/rotational-cipher/lib/RotationalCipher.pm create mode 100755 exercises/practice/rotational-cipher/t/rotational-cipher.t diff --git a/config.json b/config.json index 96d6e4cb..05285b50 100644 --- a/config.json +++ b/config.json @@ -785,6 +785,14 @@ "practices": [], "prerequisites": [], "difficulty": 2 + }, + { + "slug": "rotational-cipher", + "name": "Rotational Cipher", + "uuid": "dbc46c87-97d4-4739-ad31-e9fbc2ec7c1e", + "practices": [], + "prerequisites": [], + "difficulty": 3 } ] }, diff --git a/exercises/practice/rotational-cipher/.docs/instructions.md b/exercises/practice/rotational-cipher/.docs/instructions.md new file mode 100644 index 00000000..4bf64ca1 --- /dev/null +++ b/exercises/practice/rotational-cipher/.docs/instructions.md @@ -0,0 +1,29 @@ +# Instructions + +Create an implementation of the rotational cipher, also sometimes called the Caesar cipher. + +The Caesar cipher is a simple shift cipher that relies on transposing all the letters in the alphabet using an integer key between `0` and `26`. +Using a key of `0` or `26` will always yield the same output due to modular arithmetic. +The letter is shifted for as many values as the value of the key. + +The general notation for rotational ciphers is `ROT + `. +The most commonly used rotational cipher is `ROT13`. + +A `ROT13` on the Latin alphabet would be as follows: + +```text +Plain: abcdefghijklmnopqrstuvwxyz +Cipher: nopqrstuvwxyzabcdefghijklm +``` + +It is stronger than the Atbash cipher because it has 27 possible keys, and 25 usable keys. + +Ciphertext is written out in the same formatting as the input including spaces and punctuation. + +## Examples + +- ROT5 `omg` gives `trl` +- ROT0 `c` gives `c` +- ROT26 `Cool` gives `Cool` +- ROT13 `The quick brown fox jumps over the lazy dog.` gives `Gur dhvpx oebja sbk whzcf bire gur ynml qbt.` +- ROT13 `Gur dhvpx oebja sbk whzcf bire gur ynml qbt.` gives `The quick brown fox jumps over the lazy dog.` diff --git a/exercises/practice/rotational-cipher/.meta/config.json b/exercises/practice/rotational-cipher/.meta/config.json new file mode 100644 index 00000000..6609547a --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "glennj" + ], + "contributors": [ + "m-dango" + ], + "files": { + "solution": [ + "lib/RotationalCipher.pm" + ], + "test": [ + "t/rotational-cipher.t" + ], + "example": [ + ".meta/solutions/lib/RotationalCipher.pm" + ] + }, + "blurb": "Create an implementation of the rotational cipher, also sometimes called the Caesar cipher.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Caesar_cipher" +} diff --git a/exercises/practice/rotational-cipher/.meta/solutions/lib/RotationalCipher.pm b/exercises/practice/rotational-cipher/.meta/solutions/lib/RotationalCipher.pm new file mode 100644 index 00000000..5a808707 --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/solutions/lib/RotationalCipher.pm @@ -0,0 +1,25 @@ +package RotationalCipher; + +use strict; +use warnings; +use experimental qw; + +use Exporter qw; +our @EXPORT_OK = qw; + +# from https://perldoc.pl/perlop#tr/SEARCHLIST/REPLACEMENTLIST/cdsr +# > Because the transliteration table is built at compile time, neither the +# > SEARCHLIST nor the REPLACEMENTLIST are subjected to double quote +# > interpolation. That means that if you want to use variables, you must +# > use an eval() + +sub caesar_cipher ( $text, $shift ) { + my $alpha = 'abcdefghijklmnopqrstuvwxyz'; + my $rotated = substr( $alpha, $shift % 26 ) . substr( $alpha, 0, $shift % 26 ); + $alpha .= uc $alpha; + $rotated .= uc $rotated; + + return eval "\$text =~ tr/$alpha/$rotated/r"; +} + +1; diff --git a/exercises/practice/rotational-cipher/.meta/solutions/t/rotational-cipher.t b/exercises/practice/rotational-cipher/.meta/solutions/t/rotational-cipher.t new file mode 120000 index 00000000..b411152f --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/solutions/t/rotational-cipher.t @@ -0,0 +1 @@ +../../../t/rotational-cipher.t \ No newline at end of file diff --git a/exercises/practice/rotational-cipher/.meta/template-data.yaml b/exercises/practice/rotational-cipher/.meta/template-data.yaml new file mode 100644 index 00000000..21cfe5da --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/template-data.yaml @@ -0,0 +1,33 @@ +subs: caesar_cipher +properties: + rotate: + test: |- + use Data::Dmp; + sprintf(<<'END', dmp($case->{input}{text}), $case->{input}{shiftKey}, dmp($case->{expected}), dmp($case->{description})); + is( + caesar_cipher(%s, %s), + %s, + %s + ); + END + +stub: |- + sub caesar_cipher ($text, $shift_key) { + return undef; + } + +example: |- + # from https://perldoc.pl/perlop#tr/SEARCHLIST/REPLACEMENTLIST/cdsr + # > Because the transliteration table is built at compile time, neither the + # > SEARCHLIST nor the REPLACEMENTLIST are subjected to double quote + # > interpolation. That means that if you want to use variables, you must + # > use an eval() + + sub caesar_cipher ($text, $shift) { + my $alpha = 'abcdefghijklmnopqrstuvwxyz'; + my $rotated = substr($alpha, $shift % 26) . substr($alpha, 0, $shift % 26); + $alpha .= uc $alpha; + $rotated .= uc $rotated; + + return eval "\$text =~ tr/$alpha/$rotated/r"; + } diff --git a/exercises/practice/rotational-cipher/.meta/tests.toml b/exercises/practice/rotational-cipher/.meta/tests.toml new file mode 100644 index 00000000..53441ed2 --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/tests.toml @@ -0,0 +1,40 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[74e58a38-e484-43f1-9466-877a7515e10f] +description = "rotate a by 0, same output as input" + +[7ee352c6-e6b0-4930-b903-d09943ecb8f5] +description = "rotate a by 1" + +[edf0a733-4231-4594-a5ee-46a4009ad764] +description = "rotate a by 26, same output as input" + +[e3e82cb9-2a5b-403f-9931-e43213879300] +description = "rotate m by 13" + +[19f9eb78-e2ad-4da4-8fe3-9291d47c1709] +description = "rotate n by 13 with wrap around alphabet" + +[a116aef4-225b-4da9-884f-e8023ca6408a] +description = "rotate capital letters" + +[71b541bb-819c-4dc6-a9c3-132ef9bb737b] +description = "rotate spaces" + +[ef32601d-e9ef-4b29-b2b5-8971392282e6] +description = "rotate numbers" + +[32dd74f6-db2b-41a6-b02c-82eb4f93e549] +description = "rotate punctuation" + +[9fb93fe6-42b0-46e6-9ec1-0bf0a062d8c9] +description = "rotate all letters" diff --git a/exercises/practice/rotational-cipher/lib/RotationalCipher.pm b/exercises/practice/rotational-cipher/lib/RotationalCipher.pm new file mode 100644 index 00000000..b3011359 --- /dev/null +++ b/exercises/practice/rotational-cipher/lib/RotationalCipher.pm @@ -0,0 +1,12 @@ +package RotationalCipher; + +use v5.40; + +use Exporter qw; +our @EXPORT_OK = qw; + +sub caesar_cipher ( $text, $shift_key ) { + return undef; +} + +1; diff --git a/exercises/practice/rotational-cipher/t/rotational-cipher.t b/exercises/practice/rotational-cipher/t/rotational-cipher.t new file mode 100755 index 00000000..975fa463 --- /dev/null +++ b/exercises/practice/rotational-cipher/t/rotational-cipher.t @@ -0,0 +1,69 @@ +#!/usr/bin/env perl +use Test2::V0; + +use FindBin qw<$Bin>; +use lib "$Bin/../lib", "$Bin/../local/lib/perl5"; + +use RotationalCipher qw; + +is( # begin: 74e58a38-e484-43f1-9466-877a7515e10f + caesar_cipher( "a", 0 ), + "a", + "rotate a by 0, same output as input" +); # end: 74e58a38-e484-43f1-9466-877a7515e10f + +is( # begin: 7ee352c6-e6b0-4930-b903-d09943ecb8f5 + caesar_cipher( "a", 1 ), + "b", + "rotate a by 1" +); # end: 7ee352c6-e6b0-4930-b903-d09943ecb8f5 + +is( # begin: edf0a733-4231-4594-a5ee-46a4009ad764 + caesar_cipher( "a", 26 ), + "a", + "rotate a by 26, same output as input" +); # end: edf0a733-4231-4594-a5ee-46a4009ad764 + +is( # begin: e3e82cb9-2a5b-403f-9931-e43213879300 + caesar_cipher( "m", 13 ), + "z", + "rotate m by 13" +); # end: e3e82cb9-2a5b-403f-9931-e43213879300 + +is( # begin: 19f9eb78-e2ad-4da4-8fe3-9291d47c1709 + caesar_cipher( "n", 13 ), + "a", + "rotate n by 13 with wrap around alphabet" +); # end: 19f9eb78-e2ad-4da4-8fe3-9291d47c1709 + +is( # begin: a116aef4-225b-4da9-884f-e8023ca6408a + caesar_cipher( "OMG", 5 ), + "TRL", + "rotate capital letters" +); # end: a116aef4-225b-4da9-884f-e8023ca6408a + +is( # begin: 71b541bb-819c-4dc6-a9c3-132ef9bb737b + caesar_cipher( "O M G", 5 ), + "T R L", + "rotate spaces" +); # end: 71b541bb-819c-4dc6-a9c3-132ef9bb737b + +is( # begin: ef32601d-e9ef-4b29-b2b5-8971392282e6 + caesar_cipher( "Testing 1 2 3 testing", 4 ), + "Xiwxmrk 1 2 3 xiwxmrk", + "rotate numbers" +); # end: ef32601d-e9ef-4b29-b2b5-8971392282e6 + +is( # begin: 32dd74f6-db2b-41a6-b02c-82eb4f93e549 + caesar_cipher( "Let's eat, Grandma!", 21 ), + "Gzo'n zvo, Bmviyhv!", + "rotate punctuation" +); # end: 32dd74f6-db2b-41a6-b02c-82eb4f93e549 + +is( # begin: 9fb93fe6-42b0-46e6-9ec1-0bf0a062d8c9 + caesar_cipher( "The quick brown fox jumps over the lazy dog.", 13 ), + "Gur dhvpx oebja sbk whzcf bire gur ynml qbt.", + "rotate all letters" +); # end: 9fb93fe6-42b0-46e6-9ec1-0bf0a062d8c9 + +done_testing;