diff --git a/config.json b/config.json index 770a96c..94578d6 100644 --- a/config.json +++ b/config.json @@ -218,6 +218,14 @@ "prerequisites": [], "difficulty": 8 }, + { + "slug": "roman-numerals", + "name": "Roman Numerals", + "uuid": "f109325a-2bbb-46d7-a7ed-fe8484509985", + "practices": [], + "prerequisites": [], + "difficulty": 8 + }, { "slug": "etl", "name": "ETL", diff --git a/exercises/practice/roman-numerals/.docs/instructions.append.md b/exercises/practice/roman-numerals/.docs/instructions.append.md new file mode 100644 index 0000000..38232dc --- /dev/null +++ b/exercises/practice/roman-numerals/.docs/instructions.append.md @@ -0,0 +1,9 @@ +# SQLite specific instructions + +This exercise requires you set the `result` column of the `roman-numerals` table to the correct value based on the number found in the `number` column. + +## Table Schema + +```sql +CREATE TABLE "roman-numerals" ("number" INT, "result" TEXT); +``` diff --git a/exercises/practice/roman-numerals/.docs/instructions.md b/exercises/practice/roman-numerals/.docs/instructions.md new file mode 100644 index 0000000..50e2f5b --- /dev/null +++ b/exercises/practice/roman-numerals/.docs/instructions.md @@ -0,0 +1,12 @@ +# Introduction + +Your task is to convert a number from Arabic numerals to Roman numerals. + +For this exercise, we are only concerned about traditional Roman numerals, in which the largest number is MMMCMXCIX (or 3,999). + +~~~~exercism/note +There are lots of different ways to convert between Arabic and Roman numerals. +We recommend taking a naive approach first to familiarise yourself with the concept of Roman numerals and then search for more efficient methods. + +Make sure to check out our Deep Dive video at the end to explore the different approaches you can take! +~~~~ diff --git a/exercises/practice/roman-numerals/.docs/introduction.md b/exercises/practice/roman-numerals/.docs/introduction.md new file mode 100644 index 0000000..6fd942f --- /dev/null +++ b/exercises/practice/roman-numerals/.docs/introduction.md @@ -0,0 +1,59 @@ +# Description + +Today, most people in the world use Arabic numerals (0–9). +But if you travelled back two thousand years, you'd find that most Europeans were using Roman numerals instead. + +To write a Roman numeral we use the following Latin letters, each of which has a value: + +| M | D | C | L | X | V | I | +| ---- | --- | --- | --- | --- | --- | --- | +| 1000 | 500 | 100 | 50 | 10 | 5 | 1 | + +A Roman numeral is a sequence of these letters, and its value is the sum of the letters' values. +For example, `XVIII` has the value 18 (`10 + 5 + 1 + 1 + 1 = 18`). + +There's one rule that makes things trickier though, and that's that **the same letter cannot be used more than three times in succession**. +That means that we can't express numbers such as 4 with the seemingly natural `IIII`. +Instead, for those numbers, we use a subtraction method between two letters. +So we think of `4` not as `1 + 1 + 1 + 1` but instead as `5 - 1`. +And slightly confusingly to our modern thinking, we write the smaller number first. +This applies only in the following cases: 4 (`IV`), 9 (`IX`), 40 (`XL`), 90 (`XC`), 400 (`CD`) and 900 (`CM`). + +Order matters in Roman numerals! +Letters (and the special compounds above) must be ordered by decreasing value from left to right. + +Here are some examples: + +```text + 105 => CV +---- => -- + 100 => C ++ 5 => V +``` + +```text + 106 => CVI +---- => -- + 100 => C ++ 5 => V ++ 1 => I +``` + +```text + 104 => CIV +---- => --- + 100 => C ++ 4 => IV +``` + +And a final more complex example: + +```text + 1996 => MCMXCVI +----- => ------- + 1000 => M ++ 900 => CM ++ 90 => XC ++ 5 => V ++ 1 => I +``` diff --git a/exercises/practice/roman-numerals/.meta/config.json b/exercises/practice/roman-numerals/.meta/config.json new file mode 100644 index 0000000..1c0b3ef --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "Steffan153" + ], + "files": { + "solution": [ + "roman-numerals.sql" + ], + "test": [ + "roman-numerals_test.sql" + ], + "example": [ + ".meta/example.sql" + ], + "editor": [ + "data.csv" + ] + }, + "blurb": "Convert modern Arabic numbers into Roman numerals.", + "source": "The Roman Numeral Kata", + "source_url": "https://codingdojo.org/kata/RomanNumerals/" +} diff --git a/exercises/practice/roman-numerals/.meta/example.sql b/exercises/practice/roman-numerals/.meta/example.sql new file mode 100644 index 0000000..a197079 --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/example.sql @@ -0,0 +1,35 @@ +ALTER TABLE "roman-numerals" +ADD temp_num TEXT DEFAULT ''; + +PRAGMA recursive_triggers = ON; + +CREATE TRIGGER IF NOT EXISTS update_roman_numerals + AFTER UPDATE + ON "roman-numerals" + WHEN LENGTH(NEW.temp_num) > 0 +BEGIN + UPDATE "roman-numerals" + SET result = + -- we encode the roman numerals in base 10 + -- to add a digit, we translate IVXLC => XLCDM (multiply by ten) + -- and then add the digit at the end + + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(result, 'C', 'M'), 'L', 'D'), 'X', 'C'), 'V', 'L'), 'I', 'X') || + CASE SUBSTR(temp_num, 1, 1) + WHEN '0' THEN '' + WHEN '1' THEN 'I' + WHEN '2' THEN 'II' + WHEN '3' THEN 'III' + WHEN '4' THEN 'IV' + WHEN '5' THEN 'V' + WHEN '6' THEN 'VI' + WHEN '7' THEN 'VII' + WHEN '8' THEN 'VIII' + WHEN '9' THEN 'IX' + END, + temp_num = SUBSTR(temp_num, 2) + WHERE temp_num = NEW.temp_num; +END; + +UPDATE "roman-numerals" +SET temp_num = number || ''; diff --git a/exercises/practice/roman-numerals/.meta/tests.toml b/exercises/practice/roman-numerals/.meta/tests.toml new file mode 100644 index 0000000..709011b --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/tests.toml @@ -0,0 +1,91 @@ +# 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. + +[19828a3a-fbf7-4661-8ddd-cbaeee0e2178] +description = "1 is I" + +[f088f064-2d35-4476-9a41-f576da3f7b03] +description = "2 is II" + +[b374a79c-3bea-43e6-8db8-1286f79c7106] +description = "3 is III" + +[05a0a1d4-a140-4db1-82e8-fcc21fdb49bb] +description = "4 is IV" + +[57c0f9ad-5024-46ab-975d-de18c430b290] +description = "5 is V" + +[20a2b47f-e57f-4797-a541-0b3825d7f249] +description = "6 is VI" + +[ff3fb08c-4917-4aab-9f4e-d663491d083d] +description = "9 is IX" + +[6d1d82d5-bf3e-48af-9139-87d7165ed509] +description = "16 is XVI" + +[2bda64ca-7d28-4c56-b08d-16ce65716cf6] +description = "27 is XXVII" + +[a1f812ef-84da-4e02-b4f0-89c907d0962c] +description = "48 is XLVIII" + +[607ead62-23d6-4c11-a396-ef821e2e5f75] +description = "49 is XLIX" + +[d5b283d4-455d-4e68-aacf-add6c4b51915] +description = "59 is LIX" + +[4465ffd5-34dc-44f3-ada5-56f5007b6dad] +description = "66 is LXVI" + +[46b46e5b-24da-4180-bfe2-2ef30b39d0d0] +description = "93 is XCIII" + +[30494be1-9afb-4f84-9d71-db9df18b55e3] +description = "141 is CXLI" + +[267f0207-3c55-459a-b81d-67cec7a46ed9] +description = "163 is CLXIII" + +[902ad132-0b4d-40e3-8597-ba5ed611dd8d] +description = "166 is CLXVI" + +[cdb06885-4485-4d71-8bfb-c9d0f496b404] +description = "402 is CDII" + +[6b71841d-13b2-46b4-ba97-dec28133ea80] +description = "575 is DLXXV" + +[dacb84b9-ea1c-4a61-acbb-ce6b36674906] +description = "666 is DCLXVI" + +[432de891-7fd6-4748-a7f6-156082eeca2f] +description = "911 is CMXI" + +[e6de6d24-f668-41c0-88d7-889c0254d173] +description = "1024 is MXXIV" + +[efbe1d6a-9f98-4eb5-82bc-72753e3ac328] +description = "1666 is MDCLXVI" + +[bb550038-d4eb-4be2-a9ce-f21961ac3bc6] +description = "3000 is MMM" + +[3bc4b41c-c2e6-49d9-9142-420691504336] +description = "3001 is MMMI" + +[2f89cad7-73f6-4d1b-857b-0ef531f68b7e] +description = "3888 is MMMDCCCLXXXVIII" + +[4e18e96b-5fbb-43df-a91b-9cb511fe0856] +description = "3999 is MMMCMXCIX" diff --git a/exercises/practice/roman-numerals/create_fixture.sql b/exercises/practice/roman-numerals/create_fixture.sql new file mode 100644 index 0000000..2275fda --- /dev/null +++ b/exercises/practice/roman-numerals/create_fixture.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS "roman-numerals"; +CREATE TABLE "roman-numerals" ( + "number" INT, + "result" TEXT +); + +.mode csv +.import ./data.csv roman-numerals diff --git a/exercises/practice/roman-numerals/data.csv b/exercises/practice/roman-numerals/data.csv new file mode 100644 index 0000000..c9c74ad --- /dev/null +++ b/exercises/practice/roman-numerals/data.csv @@ -0,0 +1,27 @@ +1,"" +2,"" +3,"" +4,"" +5,"" +6,"" +9,"" +16,"" +27,"" +48,"" +49,"" +59,"" +66,"" +93,"" +141,"" +163,"" +166,"" +402,"" +575,"" +666,"" +911,"" +1024,"" +1666,"" +3000,"" +3001,"" +3888,"" +3999,"" diff --git a/exercises/practice/roman-numerals/roman-numerals.sql b/exercises/practice/roman-numerals/roman-numerals.sql new file mode 100644 index 0000000..fe2037c --- /dev/null +++ b/exercises/practice/roman-numerals/roman-numerals.sql @@ -0,0 +1,2 @@ +-- Schema: CREATE TABLE "roman-numerals" ("number" INT, "result" TEXT); +-- Task: update the roman-numerals table and set the result based on the number. diff --git a/exercises/practice/roman-numerals/roman-numerals_test.sql b/exercises/practice/roman-numerals/roman-numerals_test.sql new file mode 100644 index 0000000..af912df --- /dev/null +++ b/exercises/practice/roman-numerals/roman-numerals_test.sql @@ -0,0 +1,44 @@ +-- Setup test table and read in student solution: +.read ./test_setup.sql + +-- Test cases: +INSERT INTO tests (name, uuid, + number, expected) + VALUES + ('1 is I', '19828a3a-fbf7-4661-8ddd-cbaeee0e2178', 1, 'I'), + ('2 is II', 'f088f064-2d35-4476-9a41-f576da3f7b03', 2, 'II'), + ('3 is III', 'b374a79c-3bea-43e6-8db8-1286f79c7106', 3, 'III'), + ('4 is IV', '05a0a1d4-a140-4db1-82e8-fcc21fdb49bb', 4, 'IV'), + ('5 is V', '57c0f9ad-5024-46ab-975d-de18c430b290', 5, 'V'), + ('6 is VI', '20a2b47f-e57f-4797-a541-0b3825d7f249', 6, 'VI'), + ('9 is IX', 'ff3fb08c-4917-4aab-9f4e-d663491d083d', 9, 'IX'), + ('16 is XVI', '6d1d82d5-bf3e-48af-9139-87d7165ed509', 16, 'XVI'), + ('27 is XXVII', '2bda64ca-7d28-4c56-b08d-16ce65716cf6', 27, 'XXVII'), + ('48 is XLVIII', 'a1f812ef-84da-4e02-b4f0-89c907d0962c', 48, 'XLVIII'), + ('49 is XLIX', '607ead62-23d6-4c11-a396-ef821e2e5f75', 49, 'XLIX'), + ('59 is LIX', 'd5b283d4-455d-4e68-aacf-add6c4b51915', 59, 'LIX'), + ('66 is LXVI', '4465ffd5-34dc-44f3-ada5-56f5007b6dad', 66, 'LXVI'), + ('93 is XCIII', '46b46e5b-24da-4180-bfe2-2ef30b39d0d0', 93, 'XCIII'), + ('141 is CXLI', '30494be1-9afb-4f84-9d71-db9df18b55e3', 141, 'CXLI'), + ('163 is CLXIII', '267f0207-3c55-459a-b81d-67cec7a46ed9', 163, 'CLXIII'), + ('166 is CLXVI', '902ad132-0b4d-40e3-8597-ba5ed611dd8d', 166, 'CLXVI'), + ('402 is CDII', 'cdb06885-4485-4d71-8bfb-c9d0f496b404', 402, 'CDII'), + ('575 is DLXXV', '6b71841d-13b2-46b4-ba97-dec28133ea80', 575, 'DLXXV'), + ('666 is DCLXVI', 'dacb84b9-ea1c-4a61-acbb-ce6b36674906', 666, 'DCLXVI'), + ('911 is CMXI', '432de891-7fd6-4748-a7f6-156082eeca2f', 911, 'CMXI'), + ('1024 is MXXIV', 'e6de6d24-f668-41c0-88d7-889c0254d173', 1024, 'MXXIV'), + ('1666 is MDCLXVI', 'efbe1d6a-9f98-4eb5-82bc-72753e3ac328', 1666, 'MDCLXVI'), + ('3000 is MMM', 'bb550038-d4eb-4be2-a9ce-f21961ac3bc6', 3000, 'MMM'), + ('3001 is MMMI', '3bc4b41c-c2e6-49d9-9142-420691504336', 3001, 'MMMI'), + ('3888 is MMMDCCCLXXXVIII', '2f89cad7-73f6-4d1b-857b-0ef531f68b7e', 3888, 'MMMDCCCLXXXVIII'), + ('3999 is MMMCMXCIX', '4e18e96b-5fbb-43df-a91b-9cb511fe0856', 3999, 'MMMCMXCIX'); + +-- Comparison of user input and the tests updates the status for each test: +UPDATE tests +SET status = 'pass' +FROM (SELECT number, result FROM "roman-numerals") AS actual +WHERE (actual.number, actual.result) = (tests.number, tests.expected); + +-- Write results and debug info: +.read ./test_reporter.sql + diff --git a/exercises/practice/roman-numerals/test_reporter.sql b/exercises/practice/roman-numerals/test_reporter.sql new file mode 100644 index 0000000..6b8fffc --- /dev/null +++ b/exercises/practice/roman-numerals/test_reporter.sql @@ -0,0 +1,16 @@ +-- Update message for failed tests to give helpful information: +UPDATE tests +SET message = 'Result for ' || actual.number || ' is "' || actual.result || '", but should be: "' || tests.expected || '"' +FROM (SELECT number, result FROM "roman-numerals") AS actual +WHERE actual.number = tests.number AND tests.status = 'fail'; + +-- Save results to ./output.json (needed by the online test-runner) +.mode json +.once './output.json' +SELECT name, status, message, output, test_code, task_id +FROM tests; + +-- Display test results in readable form for the student: +.mode table +SELECT name, status, message +FROM tests; diff --git a/exercises/practice/roman-numerals/test_setup.sql b/exercises/practice/roman-numerals/test_setup.sql new file mode 100644 index 0000000..5c8009a --- /dev/null +++ b/exercises/practice/roman-numerals/test_setup.sql @@ -0,0 +1,25 @@ +-- 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 ./roman-numerals.sql +.output + +-- Create a clean testing environment: +DROP TABLE IF EXISTS tests; +CREATE TABLE IF NOT EXISTS tests ( + -- uuid and name (description) are taken from the test.toml file + uuid TEXT PRIMARY KEY, + name 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 + number INT NOT NULL, + expected TEXT NOT NULL +);