From 9856b5b8ddb49d7b2817ae99580b0ab75e41c565 Mon Sep 17 00:00:00 2001 From: Ronaldo Ferreira de Lima Date: Tue, 26 Aug 2025 12:57:28 -0300 Subject: [PATCH 1/2] * add practice exercise: rotational-cipher --- config.json | 8 ++++ .../rotational-cipher/.docs/instructions.md | 29 +++++++++++++ .../rotational-cipher/.meta/config.json | 19 ++++++++ .../rotational-cipher/.meta/example.sql | 30 +++++++++++++ .../rotational-cipher/.meta/tests.toml | 40 +++++++++++++++++ .../rotational-cipher/create_fixture.sql | 11 +++++ .../rotational-cipher/create_test_table.sql | 29 +++++++++++++ exercises/practice/rotational-cipher/data.csv | 10 +++++ .../rotational-cipher/rotational-cipher.sql | 8 ++++ .../rotational-cipher_test.sql | 43 +++++++++++++++++++ 10 files changed, 227 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/example.sql create mode 100644 exercises/practice/rotational-cipher/.meta/tests.toml create mode 100644 exercises/practice/rotational-cipher/create_fixture.sql create mode 100644 exercises/practice/rotational-cipher/create_test_table.sql create mode 100644 exercises/practice/rotational-cipher/data.csv create mode 100644 exercises/practice/rotational-cipher/rotational-cipher.sql create mode 100644 exercises/practice/rotational-cipher/rotational-cipher_test.sql diff --git a/config.json b/config.json index 3758e71..a7f35b9 100644 --- a/config.json +++ b/config.json @@ -386,6 +386,14 @@ "prerequisites": [], "difficulty": 8 }, + { + "slug": "rotational-cipher", + "name": "Rotational Cipher", + "uuid": "76fd6c44-a09b-41a7-9fec-addf26b35059", + "practices": [], + "prerequisites": [], + "difficulty": 8 + }, { "slug": "scrabble-score", "name": "Scrabble Score", diff --git a/exercises/practice/rotational-cipher/.docs/instructions.md b/exercises/practice/rotational-cipher/.docs/instructions.md new file mode 100644 index 0000000..4bf64ca --- /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 0000000..771951e --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "jimmytty" + ], + "files": { + "solution": [ + "rotational-cipher.sql" + ], + "test": [ + "rotational-cipher_test.sql" + ], + "example": [ + ".meta/example.sql" + ] + }, + "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/example.sql b/exercises/practice/rotational-cipher/.meta/example.sql new file mode 100644 index 0000000..418721c --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/example.sql @@ -0,0 +1,30 @@ +UPDATE "rotational-cipher" + SET result = ( + WITH RECURSIVE rcte (text, chr, shift) AS ( + VALUES(text, NULL, NULL) + UNION ALL + SELECT SUBSTR(text, 2), + SUBSTR(text, 1, 1), + IIF( + GLOB('[A-Za-z]', SUBSTR(text, 1, 1)), + UNICODE(SUBSTR(text, 1, 1)) + shift_key, + UNICODE(SUBSTR(text, 1, 1)) + ) + FROM rcte + WHERE text <> '' + ) + SELECT GROUP_CONCAT( + CHAR( + CASE + WHEN GLOB('[A-Z]', chr) AND shift > UNICODE('Z') + THEN shift - UNICODE('Z') + UNICODE('A') - 1 + WHEN GLOB('[a-z]', chr) AND shift > UNICODE('z') + THEN shift - UNICODE('z') + UNICODE('a') - 1 + ELSE shift + END + ), + '') + FROM rcte + WHERE chr NOTNULL + ) +; diff --git a/exercises/practice/rotational-cipher/.meta/tests.toml b/exercises/practice/rotational-cipher/.meta/tests.toml new file mode 100644 index 0000000..53441ed --- /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/create_fixture.sql b/exercises/practice/rotational-cipher/create_fixture.sql new file mode 100644 index 0000000..f8e21e6 --- /dev/null +++ b/exercises/practice/rotational-cipher/create_fixture.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS "rotational-cipher"; +CREATE TABLE "rotational-cipher" ( + text TEXT NOT NULL, + shift_key INTEGER NOT NULL, + result TEXT +); + +.mode csv +.import ./data.csv "rotational-cipher" + +UPDATE "rotational-cipher" SET result = NULL; diff --git a/exercises/practice/rotational-cipher/create_test_table.sql b/exercises/practice/rotational-cipher/create_test_table.sql new file mode 100644 index 0000000..16e081b --- /dev/null +++ b/exercises/practice/rotational-cipher/create_test_table.sql @@ -0,0 +1,29 @@ +DROP TABLE IF EXISTS tests; +CREATE TABLE IF NOT EXISTS tests ( + -- uuid and description are taken from the test.toml file + uuid TEXT PRIMARY KEY, + description TEXT NOT NULL, + -- The following section is needed by the online test-runner + status TEXT DEFAULT 'fail', + message TEXT, + output TEXT, + test_code TEXT, + task_id INTEGER DEFAULT NULL, + -- Here are columns for the actual tests + text TEXT NOT NULL, + shift_key INTEGER NOT NULL, + expected TEXT NOT NULL +); + +INSERT INTO tests (uuid, description, text, shift_key, expected) + VALUES + ('74e58a38-e484-43f1-9466-877a7515e10f', 'rotate a by 0, same output as input', 'a', 0, 'a'), + ('7ee352c6-e6b0-4930-b903-d09943ecb8f5', 'rotate a by 1', 'a', 1, 'b'), + ('edf0a733-4231-4594-a5ee-46a4009ad764', 'rotate a by 26, same output as input', 'a', 26, 'a'), + ('e3e82cb9-2a5b-403f-9931-e43213879300', 'rotate m by 13', 'm', 13, 'z'), + ('19f9eb78-e2ad-4da4-8fe3-9291d47c1709', 'rotate n by 13 with wrap around alphabet', 'n', 13, 'a'), + ('a116aef4-225b-4da9-884f-e8023ca6408a', 'rotate capital letters', 'OMG', 5, 'TRL'), + ('71b541bb-819c-4dc6-a9c3-132ef9bb737b', 'rotate spaces', 'O M G', 5, 'T R L'), + ('ef32601d-e9ef-4b29-b2b5-8971392282e6', 'rotate numbers', 'Testing 1 2 3 testing', 4, 'Xiwxmrk 1 2 3 xiwxmrk'), + ('32dd74f6-db2b-41a6-b02c-82eb4f93e549', 'rotate punctuation', 'Let''s eat, Grandma!', 21, 'Gzo''n zvo, Bmviyhv!'), + ('9fb93fe6-42b0-46e6-9ec1-0bf0a062d8c9', 'rotate all letters', 'The quick brown fox jumps over the lazy dog.', 13, 'Gur dhvpx oebja sbk whzcf bire gur ynml qbt.'); diff --git a/exercises/practice/rotational-cipher/data.csv b/exercises/practice/rotational-cipher/data.csv new file mode 100644 index 0000000..88058c9 --- /dev/null +++ b/exercises/practice/rotational-cipher/data.csv @@ -0,0 +1,10 @@ +"a",0, +"a",1, +"a",26, +"m",13, +"n",13, +"OMG",5, +"O M G",5, +"Testing 1 2 3 testing",4, +"Let's eat, Grandma!",21, +"The quick brown fox jumps over the lazy dog.",13, diff --git a/exercises/practice/rotational-cipher/rotational-cipher.sql b/exercises/practice/rotational-cipher/rotational-cipher.sql new file mode 100644 index 0000000..4fa5f5c --- /dev/null +++ b/exercises/practice/rotational-cipher/rotational-cipher.sql @@ -0,0 +1,8 @@ +-- Schema: +-- CREATE TABLE "rotational-cipher" ( +-- text TEXT NOT NULL, +-- shift_key INTEGER NOT NULL, +-- result TEXT +-- ); +-- +-- Task: update the rotational-cipher table and set the result based on text and shift_key. diff --git a/exercises/practice/rotational-cipher/rotational-cipher_test.sql b/exercises/practice/rotational-cipher/rotational-cipher_test.sql new file mode 100644 index 0000000..9541dcc --- /dev/null +++ b/exercises/practice/rotational-cipher/rotational-cipher_test.sql @@ -0,0 +1,43 @@ +-- Create database: +.read ./create_fixture.sql + +-- Read user student solution and save any output as markdown in user_output.md: +.mode markdown +.output user_output.md +.read ./rotational-cipher.sql +.output + +-- Create a clean testing environment: +.read ./create_test_table.sql + +-- Comparison of user input and the tests updates the status for each test: +UPDATE tests + SET status = 'pass' + FROM (SELECT text, shift_key, result FROM "rotational-cipher") AS actual + WHERE (actual.text, actual.shift_key, actual.result) = + (tests.text, tests.shift_key, tests.expected); + +-- Update message for failed tests to give helpful information: +UPDATE tests + SET message = ( + 'Result for "' + || PRINTF('text=%s, shift_key=%d', tests.text, tests.shift_key) + || '"' + || ' is <' || COALESCE(actual.result, 'NULL') + || '> but should be <' || tests.expected || '>' + ) + FROM (SELECT text, shift_key, result FROM "rotational-cipher") AS actual + WHERE (actual.text, actual.shift_key) = + ( tests.text, tests.shift_key) + AND tests.status = 'fail'; + +-- Save results to ./output.json (needed by the online test-runner) +.mode json +.once './output.json' +SELECT description, status, message, output, test_code, task_id +FROM tests; + +-- Display test results in readable form for the student: +.mode table +SELECT description, status, message +FROM tests; From 9a103724daed2a21e9447c6773d5b0cbfe45fa45 Mon Sep 17 00:00:00 2001 From: Ronaldo Ferreira de Lima Date: Wed, 27 Aug 2025 00:38:58 -0300 Subject: [PATCH 2/2] * fixing code style to 4 spaces indentation --- .../rotational-cipher/.meta/example.sql | 51 ++++++++++--------- .../rotational-cipher/create_fixture.sql | 6 +-- .../rotational-cipher_test.sql | 25 ++++----- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/exercises/practice/rotational-cipher/.meta/example.sql b/exercises/practice/rotational-cipher/.meta/example.sql index 418721c..dc3a7c6 100644 --- a/exercises/practice/rotational-cipher/.meta/example.sql +++ b/exercises/practice/rotational-cipher/.meta/example.sql @@ -1,30 +1,33 @@ UPDATE "rotational-cipher" - SET result = ( - WITH RECURSIVE rcte (text, chr, shift) AS ( - VALUES(text, NULL, NULL) - UNION ALL - SELECT SUBSTR(text, 2), + SET result = ( + WITH RECURSIVE rcte (text, chr, shift) AS ( + VALUES(text, NULL, NULL) + UNION ALL + SELECT + SUBSTR(text, 2), SUBSTR(text, 1, 1), IIF( - GLOB('[A-Za-z]', SUBSTR(text, 1, 1)), - UNICODE(SUBSTR(text, 1, 1)) + shift_key, - UNICODE(SUBSTR(text, 1, 1)) - ) - FROM rcte - WHERE text <> '' - ) - SELECT GROUP_CONCAT( - CHAR( - CASE - WHEN GLOB('[A-Z]', chr) AND shift > UNICODE('Z') - THEN shift - UNICODE('Z') + UNICODE('A') - 1 - WHEN GLOB('[a-z]', chr) AND shift > UNICODE('z') - THEN shift - UNICODE('z') + UNICODE('a') - 1 - ELSE shift - END - ), - '') - FROM rcte + GLOB('[A-Za-z]', SUBSTR(text, 1, 1)), + UNICODE(SUBSTR(text, 1, 1)) + shift_key, + UNICODE(SUBSTR(text, 1, 1)) + ) + FROM rcte + WHERE text <> '' + ) + SELECT + GROUP_CONCAT( + CHAR( + CASE + WHEN GLOB('[A-Z]', chr) AND shift > UNICODE('Z') + THEN shift - UNICODE('Z') + UNICODE('A') - 1 + WHEN GLOB('[a-z]', chr) AND shift > UNICODE('z') + THEN shift - UNICODE('z') + UNICODE('a') - 1 + ELSE shift + END + ), + '' + ) + FROM rcte WHERE chr NOTNULL ) ; diff --git a/exercises/practice/rotational-cipher/create_fixture.sql b/exercises/practice/rotational-cipher/create_fixture.sql index f8e21e6..7c25a2a 100644 --- a/exercises/practice/rotational-cipher/create_fixture.sql +++ b/exercises/practice/rotational-cipher/create_fixture.sql @@ -1,8 +1,8 @@ DROP TABLE IF EXISTS "rotational-cipher"; CREATE TABLE "rotational-cipher" ( - text TEXT NOT NULL, - shift_key INTEGER NOT NULL, - result TEXT + text TEXT NOT NULL, + shift_key INTEGER NOT NULL, + result TEXT ); .mode csv diff --git a/exercises/practice/rotational-cipher/rotational-cipher_test.sql b/exercises/practice/rotational-cipher/rotational-cipher_test.sql index 9541dcc..5f4d143 100644 --- a/exercises/practice/rotational-cipher/rotational-cipher_test.sql +++ b/exercises/practice/rotational-cipher/rotational-cipher_test.sql @@ -12,24 +12,21 @@ -- Comparison of user input and the tests updates the status for each test: UPDATE tests - SET status = 'pass' - FROM (SELECT text, shift_key, result FROM "rotational-cipher") AS actual - WHERE (actual.text, actual.shift_key, actual.result) = + SET status = 'pass' + FROM (SELECT text, shift_key, result FROM "rotational-cipher") AS actual + WHERE (actual.text, actual.shift_key, actual.result) = (tests.text, tests.shift_key, tests.expected); -- Update message for failed tests to give helpful information: UPDATE tests - SET message = ( - 'Result for "' - || PRINTF('text=%s, shift_key=%d', tests.text, tests.shift_key) - || '"' - || ' is <' || COALESCE(actual.result, 'NULL') - || '> but should be <' || tests.expected || '>' - ) - FROM (SELECT text, shift_key, result FROM "rotational-cipher") AS actual - WHERE (actual.text, actual.shift_key) = - ( tests.text, tests.shift_key) - AND tests.status = 'fail'; + SET message = ( + 'Result for "' + || PRINTF('text=%s, shift_key=%d', tests.text, tests.shift_key) + || '"' || ' is <' || COALESCE(actual.result, 'NULL') + || '> but should be <' || tests.expected || '>' ) + FROM (SELECT text, shift_key, result FROM "rotational-cipher") AS actual + WHERE (actual.text, actual.shift_key) = (tests.text, tests.shift_key) + AND tests.status = 'fail'; -- Save results to ./output.json (needed by the online test-runner) .mode json