diff --git a/config.json b/config.json index 45b64273..1b99f98f 100644 --- a/config.json +++ b/config.json @@ -194,6 +194,14 @@ "prerequisites": [], "difficulty": 8 }, + { + "slug": "all-your-base", + "name": "All Your Base", + "uuid": "36b4c4c7-c5a1-4885-bf2e-2b77130ab23a", + "practices": [], + "prerequisites": [], + "difficulty": 8 + }, { "slug": "anagram", "name": "Anagram", diff --git a/exercises/practice/all-your-base/.docs/instructions.append.md b/exercises/practice/all-your-base/.docs/instructions.append.md new file mode 100644 index 00000000..5a97e8d3 --- /dev/null +++ b/exercises/practice/all-your-base/.docs/instructions.append.md @@ -0,0 +1,33 @@ +# SQLite-specific instructions + +* The **digits** column contains a JSON-encoded list of integers. + Example: + ```json + [1,0,1,0,1,0] + ``` +* The **result** column should contain JSON-encoded data: an object with the digits as integers or a descripition of any errors. + Examples: + ```json + {"digits":[1,0,1,0,1,0]} + ``` + or + ```json + {"error":"some error description"} + ``` + +## Table Schema + +```sql +CREATE TABLE IF NOT EXISTS "all-your-base" ( + input_base INTEGER NOT NULL, + digits TEXT NOT NULL, -- json array + output_base INTEGER NOT NULL, + result TEXT -- json object +); +``` + +## JSON documentation + +[JSON Functions And Operators][json-docs] + +[json-docs]: https://www.sqlite.org/json1.htm diff --git a/exercises/practice/all-your-base/.docs/instructions.md b/exercises/practice/all-your-base/.docs/instructions.md new file mode 100644 index 00000000..1b688b69 --- /dev/null +++ b/exercises/practice/all-your-base/.docs/instructions.md @@ -0,0 +1,28 @@ +# Instructions + +Convert a sequence of digits in one base, representing a number, into a sequence of digits in another base, representing the same number. + +~~~~exercism/note +Try to implement the conversion yourself. +Do not use something else to perform the conversion for you. +~~~~ + +## About [Positional Notation][positional-notation] + +In positional notation, a number in base **b** can be understood as a linear combination of powers of **b**. + +The number 42, _in base 10_, means: + +`(4 × 10¹) + (2 × 10⁰)` + +The number 101010, _in base 2_, means: + +`(1 × 2⁵) + (0 × 2⁴) + (1 × 2³) + (0 × 2²) + (1 × 2¹) + (0 × 2⁰)` + +The number 1120, _in base 3_, means: + +`(1 × 3³) + (1 × 3²) + (2 × 3¹) + (0 × 3⁰)` + +_Yes. Those three numbers above are exactly the same. Congratulations!_ + +[positional-notation]: https://en.wikipedia.org/wiki/Positional_notation diff --git a/exercises/practice/all-your-base/.docs/introduction.md b/exercises/practice/all-your-base/.docs/introduction.md new file mode 100644 index 00000000..68aaffbe --- /dev/null +++ b/exercises/practice/all-your-base/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You've just been hired as professor of mathematics. +Your first week went well, but something is off in your second week. +The problem is that every answer given by your students is wrong! +Luckily, your math skills have allowed you to identify the problem: the student answers _are_ correct, but they're all in base 2 (binary)! +Amazingly, it turns out that each week, the students use a different base. +To help you quickly verify the student answers, you'll be building a tool to translate between bases. diff --git a/exercises/practice/all-your-base/.meta/config.json b/exercises/practice/all-your-base/.meta/config.json new file mode 100644 index 00000000..deb256ec --- /dev/null +++ b/exercises/practice/all-your-base/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "jimmytty" + ], + "files": { + "solution": [ + "all-your-base.sql" + ], + "test": [ + "all-your-base_test.sql" + ], + "example": [ + ".meta/example.sql" + ] + }, + "blurb": "Convert a number, represented as a sequence of digits in one base, to any other base." +} diff --git a/exercises/practice/all-your-base/.meta/example.sql b/exercises/practice/all-your-base/.meta/example.sql new file mode 100644 index 00000000..4fcc2d63 --- /dev/null +++ b/exercises/practice/all-your-base/.meta/example.sql @@ -0,0 +1,121 @@ +UPDATE "all-your-base" + SET result = JSON_OBJECT('error', 'input base must be >= 2') + WHERE input_base < 2 + AND result ISNULL +; +UPDATE "all-your-base" + SET result = JSON_OBJECT('error', 'output base must be >= 2') + WHERE output_base < 2 + AND result ISNULL +; +UPDATE "all-your-base" + SET result = JSON_OBJECT( + 'error', 'all digits must satisfy 0 <= d < input base' + ) + WHERE ( + SELECT COUNT(*) + FROM ( + SELECT input_base, j.VALUE AS digit + FROM JSON_EACH(digits) j + ) + WHERE digit < 0 OR digit >= input_base + ) != 0 + AND result ISNULL +; + +UPDATE "all-your-base" + SET result = JSON_OBJECT('digits', JSON_ARRAY(0)) + WHERE ( + SELECT COUNT(v) + FROM ( + SELECT j.VALUE v FROM JSON_EACH(digits) j + ) + WHERE v != 0 + ) = 0 + AND result ISNULL + ; + +UPDATE "all-your-base" + SET result = JSON_OBJECT('digits', ( + WITH RECURSIVE rcte (n, d) AS ( + VALUES(( + SELECT + CAST(SUM(digit * POW(input_base, row_number - 1)) AS INTEGER) + FROM ( + SELECT row_number() over() AS row_number, * + FROM ( + SELECT j.VALUE AS digit + FROM json_each(digits) j + ORDER BY rowid DESC + ) + ) + ), -1) + UNION ALL + SELECT + CAST(n % pow(10, CAST(log10(n) AS INT)) AS INT), + CAST(n / pow(10, CAST(log10(n) AS INT)) AS INT) + FROM rcte + WHERE n != 0 + ) + SELECT JSON_GROUP_ARRAY(d) FROM rcte WHERE d > -1 + )) + WHERE output_base = 10 + AND result ISNULL + ; + +UPDATE "all-your-base" + SET result = JSON_OBJECT('digits', ( + WITH RECURSIVE rcte (n, d) AS ( + VALUES(( + SELECT GROUP_CONCAT(j.VALUE, '') * 1 + FROM JSON_EACH(digits) j + ), -1) + UNION ALL + SELECT n / output_base, n % output_base + FROM rcte + WHERE n > 0 + ) + SELECT JSON_GROUP_ARRAY(d) + FROM ( + SELECT d + FROM rcte + WHERE d > -1 + ORDER BY row_number() OVER () DESC + ) + )) + WHERE input_base = 10 + AND result ISNULL + ; + +UPDATE "all-your-base" + SET result = JSON_OBJECT('digits', ( + WITH RECURSIVE rcte (n, d) AS ( + VALUES(( + SELECT + CAST(SUM(digit * POW(input_base, row_number - 1)) AS INTEGER) + FROM ( + SELECT row_number() over() AS row_number, * + FROM ( + SELECT j.VALUE AS digit + FROM json_each(digits) j + ORDER BY rowid DESC + ) + ) + ), -1) + UNION ALL + SELECT n / output_base, n % output_base + FROM rcte + WHERE n > 0 + ) + SELECT JSON_GROUP_ARRAY(d) + FROM ( + SELECT d + FROM rcte + WHERE d > -1 + ORDER BY row_number() OVER () DESC + ) + )) + WHERE input_base != 10 + AND output_base != 10 + AND result ISNULL +; diff --git a/exercises/practice/all-your-base/.meta/tests.toml b/exercises/practice/all-your-base/.meta/tests.toml new file mode 100644 index 00000000..8968c130 --- /dev/null +++ b/exercises/practice/all-your-base/.meta/tests.toml @@ -0,0 +1,73 @@ +# 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. + +[5ce422f9-7a4b-4f44-ad29-49c67cb32d2c] +description = "single bit one to decimal" + +[0cc3fea8-bb79-46ac-a2ab-5a2c93051033] +description = "binary to single decimal" + +[f12db0f9-0d3d-42c2-b3ba-e38cb375a2b8] +description = "single decimal to binary" + +[2c45cf54-6da3-4748-9733-5a3c765d925b] +description = "binary to multiple decimal" + +[65ddb8b4-8899-4fcc-8618-181b2cf0002d] +description = "decimal to binary" + +[8d418419-02a7-4824-8b7a-352d33c6987e] +description = "trinary to hexadecimal" + +[d3901c80-8190-41b9-bd86-38d988efa956] +description = "hexadecimal to trinary" + +[5d42f85e-21ad-41bd-b9be-a3e8e4258bbf] +description = "15-bit integer" + +[d68788f7-66dd-43f8-a543-f15b6d233f83] +description = "empty list" + +[5e27e8da-5862-4c5f-b2a9-26c0382b6be7] +description = "single zero" + +[2e1c2573-77e4-4b9c-8517-6c56c5bcfdf2] +description = "multiple zeros" + +[3530cd9f-8d6d-43f5-bc6e-b30b1db9629b] +description = "leading zeros" + +[a6b476a1-1901-4f2a-92c4-4d91917ae023] +description = "input base is one" + +[e21a693a-7a69-450b-b393-27415c26a016] +description = "input base is zero" + +[54a23be5-d99e-41cc-88e0-a650ffe5fcc2] +description = "input base is negative" + +[9eccf60c-dcc9-407b-95d8-c37b8be56bb6] +description = "negative digit" + +[232fa4a5-e761-4939-ba0c-ed046cd0676a] +description = "invalid positive digit" + +[14238f95-45da-41dc-95ce-18f860b30ad3] +description = "output base is one" + +[73dac367-da5c-4a37-95fe-c87fad0a4047] +description = "output base is zero" + +[13f81f42-ff53-4e24-89d9-37603a48ebd9] +description = "output base is negative" + +[0e6c895d-8a5d-4868-a345-309d094cfe8d] +description = "both bases are negative" diff --git a/exercises/practice/all-your-base/all-your-base.sql b/exercises/practice/all-your-base/all-your-base.sql new file mode 100644 index 00000000..c13fa2e1 --- /dev/null +++ b/exercises/practice/all-your-base/all-your-base.sql @@ -0,0 +1,11 @@ +-- Schema: CREATE TABLE IF NOT EXISTS "all-your-base" ( +-- input_base INTEGER NOT NULL, +-- digits TEXT NOT NULL, -- json array +-- output_base INTEGER NOT NULL, +-- result TEXT -- json object +-- ); +-- Task: update the all-your-base table and set the result based on converting +-- digits from input_base to output_base. +-- * the digits column contains a JSON-encoded list of integers. +-- * the result column should contain JSON-encoded data: an +-- object with the digits as integers or a descripition of any errors. diff --git a/exercises/practice/all-your-base/all-your-base_test.sql b/exercises/practice/all-your-base/all-your-base_test.sql new file mode 100644 index 00000000..9a1439e9 --- /dev/null +++ b/exercises/practice/all-your-base/all-your-base_test.sql @@ -0,0 +1,44 @@ +-- 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 ./all-your-base.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 input_base, digits, output_base, result FROM "all-your-base") AS actual +WHERE (actual.input_base, actual.digits, actual.output_base, JSON(actual.result)) = (tests.input_base, tests.digits, tests.output_base, JSON(tests.expected)); + +-- Update message for failed tests to give helpful information: +UPDATE tests +SET message = ( + 'Result for "' + || JSON_OBJECT( + 'input_base', tests.input_base, + 'digits', JSON(tests.digits), + 'output_base', tests.output_base + ) + || '"' + || ' is <' || COALESCE(actual.result, 'NULL') + || '> but should be <' || tests.expected || '>' +) +FROM (SELECT input_base, digits, output_base, result FROM "all-your-base") AS actual +WHERE (actual.input_base, actual.digits, actual.output_base) = (tests.input_base, tests.digits, tests.output_base) 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; diff --git a/exercises/practice/all-your-base/create_fixture.sql b/exercises/practice/all-your-base/create_fixture.sql new file mode 100644 index 00000000..1da7b94d --- /dev/null +++ b/exercises/practice/all-your-base/create_fixture.sql @@ -0,0 +1,12 @@ +DROP TABLE IF EXISTS "all-your-base"; +CREATE TABLE "all-your-base" ( + input_base INTEGER NOT NULL, + digits TEXT NOT NULL, -- json array + output_base INTEGER NOT NULL, + result TEXT -- json object +); + +.mode csv +.import ./data.csv "all-your-base" + +UPDATE "all-your-base" SET result = NULL; diff --git a/exercises/practice/all-your-base/create_test_table.sql b/exercises/practice/all-your-base/create_test_table.sql new file mode 100644 index 00000000..b0f41079 --- /dev/null +++ b/exercises/practice/all-your-base/create_test_table.sql @@ -0,0 +1,41 @@ +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 + input_base INTEGER NOT NULL, + digits TEXT NOT NULL, -- json array + output_base INTEGER NOT NULL, + expected TEXT -- json object +); + +INSERT INTO tests (uuid, description, input_base, digits, output_base, expected) +VALUES + ('5ce422f9-7a4b-4f44-ad29-49c67cb32d2c','single bit one to decimal',2,'[1]',10,'{"digits":[1]}'), + ('0cc3fea8-bb79-46ac-a2ab-5a2c93051033','binary to single decimal',2,'[1,0,1]',10,'{"digits":[5]}'), + ('f12db0f9-0d3d-42c2-b3ba-e38cb375a2b8','single decimal to binary',10,'[5]',2,'{"digits":[1,0,1]}'), + ('2c45cf54-6da3-4748-9733-5a3c765d925b','binary to multiple decimal',2,'[1,0,1,0,1,0]',10,'{"digits":[4,2]}'), + ('65ddb8b4-8899-4fcc-8618-181b2cf0002d','decimal to binary',10,'[4,2]',2,'{"digits":[1,0,1,0,1,0]}'), + ('8d418419-02a7-4824-8b7a-352d33c6987e','trinary to hexadecimal',3,'[1,1,2,0]',16,'{"digits":[2,10]}'), + ('d3901c80-8190-41b9-bd86-38d988efa956','hexadecimal to trinary',16,'[2,10]',3,'{"digits":[1,1,2,0]}'), + ('5d42f85e-21ad-41bd-b9be-a3e8e4258bbf','15-bit integer',97,'[3,46,60]',73,'{"digits":[6,10,45]}'), + ('d68788f7-66dd-43f8-a543-f15b6d233f83','empty list',2,'[]',10,'{"digits":[0]}'), + ('5e27e8da-5862-4c5f-b2a9-26c0382b6be7','single zero',10,'[0]',2,'{"digits":[0]}'), + ('2e1c2573-77e4-4b9c-8517-6c56c5bcfdf2','multiple zeros',10,'[0,0,0]',2,'{"digits":[0]}'), + ('3530cd9f-8d6d-43f5-bc6e-b30b1db9629b','leading zeros',7,'[0,6,0]',10,'{"digits":[4,2]}'), + ('a6b476a1-1901-4f2a-92c4-4d91917ae023','input base is one',1,'[0]',10,'{"error":"input base must be >= 2"}'), + ('e21a693a-7a69-450b-b393-27415c26a016','input base is zero',0,'[]',10,'{"error":"input base must be >= 2"}'), + ('54a23be5-d99e-41cc-88e0-a650ffe5fcc2','input base is negative',-2,'[1]',10,'{"error":"input base must be >= 2"}'), + ('9eccf60c-dcc9-407b-95d8-c37b8be56bb6','negative digit',2,'[1,-1,1,0,1,0]',10,'{"error":"all digits must satisfy 0 <= d < input base"}'), + ('232fa4a5-e761-4939-ba0c-ed046cd0676a','invalid positive digit',2,'[1,2,1,0,1,0]',10,'{"error":"all digits must satisfy 0 <= d < input base"}'), + ('14238f95-45da-41dc-95ce-18f860b30ad3','output base is one',2,'[1,0,1,0,1,0]',1,'{"error":"output base must be >= 2"}'), + ('73dac367-da5c-4a37-95fe-c87fad0a4047','output base is zero',10,'[7]',0,'{"error":"output base must be >= 2"}'), + ('13f81f42-ff53-4e24-89d9-37603a48ebd9','output base is negative',2,'[1]',-7,'{"error":"output base must be >= 2"}'), + ('0e6c895d-8a5d-4868-a345-309d094cfe8d','both bases are negative',-2,'[1]',-7,'{"error":"input base must be >= 2"}'); diff --git a/exercises/practice/all-your-base/data.csv b/exercises/practice/all-your-base/data.csv new file mode 100644 index 00000000..93f94fb2 --- /dev/null +++ b/exercises/practice/all-your-base/data.csv @@ -0,0 +1,21 @@ +2,"[1]",10,"" +2,"[1,0,1]",10,"" +10,"[5]",2,"" +2,"[1,0,1,0,1,0]",10,"" +10,"[4,2]",2,"" +3,"[1,1,2,0]",16,"" +16,"[2,10]",3,"" +97,"[3,46,60]",73,"" +2,"[]",10,"" +10,"[0]",2,"" +10,"[0,0,0]",2,"" +7,"[0,6,0]",10,"" +1,"[0]",10,"" +0,"[]",10,"" +-2,"[1]",10,"" +2,"[1,-1,1,0,1,0]",10,"" +2,"[1,2,1,0,1,0]",10,"" +2,"[1,0,1,0,1,0]",1,"" +10,"[7]",0,"" +2,"[1]",-7,"" +-2,"[1]",-7,""