Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,14 @@
"prerequisites": [],
"difficulty": 8
},
{
"slug": "crypto-square",
"name": "Crypto Square",
"uuid": "9fc46c57-3011-4dff-b4af-9259aeb2e170",
"practices": [],
"prerequisites": [],
"difficulty": 8
},
{
"slug": "diamond",
"name": "Diamond",
Expand Down
71 changes: 71 additions & 0 deletions exercises/practice/crypto-square/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Instructions

Implement the classic method for composing secret messages called a square code.

Given an English text, output the encoded version of that text.

First, the input is normalized: the spaces and punctuation are removed from the English text and the message is down-cased.

Then, the normalized characters are broken into rows.
These rows can be regarded as forming a rectangle when printed with intervening newlines.

For example, the sentence

```text
"If man was meant to stay on the ground, god would have given us roots."
```

is normalized to:

```text
"ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots"
```

The plaintext should be organized into a rectangle as square as possible.
The size of the rectangle should be decided by the length of the message.

If `c` is the number of columns and `r` is the number of rows, then for the rectangle `r` x `c` find the smallest possible integer `c` such that:

- `r * c >= length of message`,
- and `c >= r`,
- and `c - r <= 1`.

Our normalized text is 54 characters long, dictating a rectangle with `c = 8` and `r = 7`:

```text
"ifmanwas"
"meanttos"
"tayonthe"
"groundgo"
"dwouldha"
"vegivenu"
"sroots "
```

The coded message is obtained by reading down the columns going left to right.

The message above is coded as:

```text
"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau"
```

Output the encoded text in chunks that fill perfect rectangles `(r X c)`, with `c` chunks of `r` length, separated by spaces.
For phrases that are `n` characters short of the perfect rectangle, pad each of the last `n` chunks with a single trailing space.

```text
"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
```

Notice that were we to stack these, we could visually decode the ciphertext back in to the original message:

```text
"imtgdvs"
"fearwer"
"mayoogo"
"anouuio"
"ntnnlvt"
"wttddes"
"aohghn "
"sseoau "
```
19 changes: 19 additions & 0 deletions exercises/practice/crypto-square/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"jimmytty"
],
"files": {
"solution": [
"crypto-square.sql"
],
"test": [
"crypto-square_test.sql"
],
"example": [
".meta/example.sql"
]
},
"blurb": "Implement the classic method for composing secret messages called a square code.",
"source": "J Dalbey's Programming Practice problems",
"source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
}
106 changes: 106 additions & 0 deletions exercises/practice/crypto-square/.meta/example.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
DROP TABLE IF EXISTS tmp;
CREATE TEMPORARY TABLE tmp (
plaintext TEXT PRIMARY KEY,
normalized TEXT
);
INSERT INTO tmp
SELECT plaintext, COALESCE(
(WITH RECURSIVE rcte (string, chr) AS (
VALUES (LOWER(plaintext), NULL)
UNION ALL
SELECT SUBSTR(string, 2),
IIF(GLOB('[^0-9a-z]', SUBSTR(string, 1, 1)),
NULL,
SUBSTR(string, 1, 1))
FROM rcte
WHERE string <> ''
)
SELECT GROUP_CONCAT(chr, '')
FROM rcte
WHERE chr NOTNULL
), '')
FROM "crypto-square"
;

ALTER TABLE tmp ADD COLUMN cols INTEGER;
UPDATE tmp SET cols = ROUND(CEIL(sqrt(LENGTH(normalized))));

ALTER TABLE tmp ADD COLUMN square TEXT;
WITH to_square (plaintext, string) AS (
SELECT plaintext,
(WITH RECURSIVE rcte (string, chr) AS (
VALUES (tmp.normalized, NULL)
UNION ALL
SELECT SUBSTR(string, 2), SUBSTR(string, 1, 1)
FROM rcte
WHERE string <> ''
)
SELECT GROUP_CONCAT(chunk, CHAR(10))
FROM (
SELECT PRINTF('%-*s', tmp.cols, GROUP_CONCAT(chr, '')) chunk
FROM (
SELECT (ROW_NUMBER() OVER () - 1) / tmp.cols AS gid, chr
FROM rcte
WHERE chr NOTNULL
)
GROUP BY gid
)
)
FROM tmp
)
UPDATE tmp
SET square = string
FROM to_square
WHERE tmp.plaintext = to_square.plaintext
;

ALTER TABLE tmp ADD COLUMN matrix TEXT;
WITH to_matrix (plaintext, matrix) AS (
SELECT plaintext,
(WITH RECURSIVE split_lines (text, array) AS (
VALUES (tmp.square || CHAR(10), NULL)
UNION ALL
SELECT
SUBSTR(text, INSTR(text, CHAR(10))+1),
(WITH RECURSIVE split_chars (string, chr) AS (
VALUES (PRINTF('%-*s',
tmp.cols,
SUBSTR(text, 1, INSTR(text, CHAR(10)) - 1)),
NULL)
UNION ALL
SELECT SUBSTR(string, 2), SUBSTR(string, 1, 1)
FROM split_chars
WHERE string <> ''
) SELECT JSON_GROUP_ARRAY(chr) FROM split_chars WHERE chr NOTNULL)
FROM split_lines
WHERE text <> ''
)
SELECT JSON_GROUP_ARRAY(JSON(array))
FROM split_lines
WHERE array NOTNULL
)
FROM tmp
)
UPDATE tmp
SET matrix = to_matrix.matrix
FROM to_matrix
WHERE tmp.plaintext = to_matrix.plaintext
;

WITH to_encode (plaintext, encoded) AS (
SELECT plaintext, COALESCE(
(SELECT GROUP_CONCAT(line, ' ')
FROM (
SELECT GROUP_CONCAT(j.value, '') line
FROM JSON_TREE(tmp.matrix) j
WHERE j.type <> 'array'
GROUP BY SUBSTR(j.fullkey, INSTR(j.fullkey, ']['))
)
), '')
FROM tmp
)
UPDATE "crypto-square" AS cs
SET result = encoded
FROM to_encode AS te
WHERE cs.plaintext = te.plaintext
;
39 changes: 39 additions & 0 deletions exercises/practice/crypto-square/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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.

[407c3837-9aa7-4111-ab63-ec54b58e8e9f]
description = "empty plaintext results in an empty ciphertext"

[aad04a25-b8bb-4304-888b-581bea8e0040]
description = "normalization results in empty plaintext"

[64131d65-6fd9-4f58-bdd8-4a2370fb481d]
description = "Lowercase"

[63a4b0ed-1e3c-41ea-a999-f6f26ba447d6]
description = "Remove spaces"

[1b5348a1-7893-44c1-8197-42d48d18756c]
description = "Remove punctuation"

[8574a1d3-4a08-4cec-a7c7-de93a164f41a]
description = "9 character plaintext results in 3 chunks of 3 characters"

[a65d3fa1-9e09-43f9-bcec-7a672aec3eae]
description = "8 character plaintext results in 3 chunks, the last one with a trailing space"

[fbcb0c6d-4c39-4a31-83f6-c473baa6af80]
description = "54 character plaintext results in 7 chunks, the last two with trailing spaces"
include = false

[33fd914e-fa44-445b-8f38-ff8fbc9fe6e6]
description = "54 character plaintext results in 8 chunks, the last two with trailing spaces"
reimplements = "fbcb0c6d-4c39-4a31-83f6-c473baa6af80"
10 changes: 10 additions & 0 deletions exercises/practice/crypto-square/create_fixture.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
DROP TABLE IF EXISTS "crypto-square";
CREATE TABLE "crypto-square" (
plaintext TEXT NOT NULL,
result TEXT
);

.mode csv
.import ./data.csv "crypto-square"

UPDATE "crypto-square" SET result = NULL;
27 changes: 27 additions & 0 deletions exercises/practice/crypto-square/create_test_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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
plaintext TEXT NOT NULL,
expected TEXT NOT NULL
);


INSERT INTO tests (uuid, description, plaintext, expected)
VALUES
('407c3837-9aa7-4111-ab63-ec54b58e8e9f', 'empty plaintext results in an empty ciphertext', '', ''),
('aad04a25-b8bb-4304-888b-581bea8e0040', 'normalization results in empty plaintext', '... --- ...', ''),
('64131d65-6fd9-4f58-bdd8-4a2370fb481d', 'Lowercase', 'A', 'a'),
('63a4b0ed-1e3c-41ea-a999-f6f26ba447d6', 'Remove spaces', ' b ', 'b'),
('1b5348a1-7893-44c1-8197-42d48d18756c', 'Remove punctuation', '@1,%!', '1'),
('8574a1d3-4a08-4cec-a7c7-de93a164f41a', '9 character plaintext results in 3 chunks of 3 characters', 'This is fun!', 'tsf hiu isn'),
('a65d3fa1-9e09-43f9-bcec-7a672aec3eae', '8 character plaintext results in 3 chunks, the last one with a trailing space', 'Chill out.', 'clu hlt io '),
('33fd914e-fa44-445b-8f38-ff8fbc9fe6e6', '54 character plaintext results in 8 chunks, the last two with trailing spaces', 'If man was meant to stay on the ground, god would have given us roots.', 'imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau ');
7 changes: 7 additions & 0 deletions exercises/practice/crypto-square/crypto-square.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- Schema:
-- CREATE TABLE "crypto-square" (
-- plaintext TEXT NOT NULL,
-- result TEXT
-- );
--
-- Task: update the crypto-square table and set the result column based on the plaintext.
40 changes: 40 additions & 0 deletions exercises/practice/crypto-square/crypto-square_test.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-- 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 ./crypto-square.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 plaintext, result FROM "crypto-square") AS actual
WHERE (actual.plaintext, actual.result) = (tests.plaintext, tests.expected);

-- Update message for failed tests to give helpful information:
UPDATE tests
SET message = (
'Result for "'
|| tests.plaintext
|| '"' || ' is <'
|| COALESCE(actual.result, 'NULL')
|| '> but should be <'
|| tests.expected
|| '>'
)
FROM (SELECT plaintext, result FROM "crypto-square") AS actual
WHERE actual.plaintext = tests.plaintext 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;
8 changes: 8 additions & 0 deletions exercises/practice/crypto-square/data.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"",
"... --- ...",
"A",
" b ",
"@1,%!",
"This is fun!",
"Chill out.",
"If man was meant to stay on the ground, god would have given us roots.",