diff --git a/config.json b/config.json index 194d675..440c652 100644 --- a/config.json +++ b/config.json @@ -234,6 +234,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "perfect-numbers", + "name": "Perfect Numbers", + "uuid": "309666ba-cbde-429b-ae07-ddb05366bf0a", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "resistor-color-trio", "name": "Resistor Color Trio", @@ -421,7 +429,7 @@ { "slug": "run-length-encoding", "name": "Run-Length Encoding", - "uuid": "065feae7-f6fc-46b9-a226-d3d9df9d192b", + "uuid": "065feae7-f6fc-46b9-a226-d3d9df9d192b", "practices": [], "prerequisites": [], "difficulty": 8 diff --git a/exercises/practice/perfect-numbers/.docs/instructions.md b/exercises/practice/perfect-numbers/.docs/instructions.md new file mode 100644 index 0000000..b2bc82c --- /dev/null +++ b/exercises/practice/perfect-numbers/.docs/instructions.md @@ -0,0 +1,39 @@ +# Instructions + +Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers. + +The Greek mathematician [Nicomachus][nicomachus] devised a classification scheme for positive integers, identifying each as belonging uniquely to the categories of [perfect](#perfect), [abundant](#abundant), or [deficient](#deficient) based on their [aliquot sum][aliquot-sum]. +The _aliquot sum_ is defined as the sum of the factors of a number not including the number itself. +For example, the aliquot sum of `15` is `1 + 3 + 5 = 9`. + +## Perfect + +A number is perfect when it equals its aliquot sum. +For example: + +- `6` is a perfect number because `1 + 2 + 3 = 6` +- `28` is a perfect number because `1 + 2 + 4 + 7 + 14 = 28` + +## Abundant + +A number is abundant when it is less than its aliquot sum. +For example: + +- `12` is an abundant number because `1 + 2 + 3 + 4 + 6 = 16` +- `24` is an abundant number because `1 + 2 + 3 + 4 + 6 + 8 + 12 = 36` + +## Deficient + +A number is deficient when it is greater than its aliquot sum. +For example: + +- `8` is a deficient number because `1 + 2 + 4 = 7` +- Prime numbers are deficient + +## Task + +Implement a way to determine whether a given number is [perfect](#perfect). +Depending on your language track, you may also need to implement a way to determine whether a given number is [abundant](#abundant) or [deficient](#deficient). + +[nicomachus]: https://en.wikipedia.org/wiki/Nicomachus +[aliquot-sum]: https://en.wikipedia.org/wiki/Aliquot_sum diff --git a/exercises/practice/perfect-numbers/.meta/config.json b/exercises/practice/perfect-numbers/.meta/config.json new file mode 100644 index 0000000..bfcc0ba --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "jimmytty" + ], + "files": { + "solution": [ + "perfect-numbers.sql" + ], + "test": [ + "perfect-numbers_test.sql" + ], + "example": [ + ".meta/example.sql" + ] + }, + "blurb": "Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers.", + "source": "Taken from Chapter 2 of Functional Thinking by Neal Ford.", + "source_url": "https://www.oreilly.com/library/view/functional-thinking/9781449365509/" +} diff --git a/exercises/practice/perfect-numbers/.meta/example.sql b/exercises/practice/perfect-numbers/.meta/example.sql new file mode 100644 index 0000000..ed8a539 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/example.sql @@ -0,0 +1,31 @@ +UPDATE "perfect-numbers" + SET error = 'Classification is only possible for positive integers.' + WHERE number < 1 +; + +UPDATE "perfect-numbers" + SET result = 'deficient' + WHERE number = 1 + AND error ISNULL +; + +UPDATE "perfect-numbers" + SET result = ( + WITH + factors AS ( + SELECT IIF(number % value = 0, value, 0) divisor + FROM GENERATE_SERIES(1, number / 2)), + aliquot_sums AS ( + SELECT SUM(divisor) aliquot_sum + FROM factors + ) + SELECT CASE + WHEN number = aliquot_sum THEN 'perfect' + WHEN number < aliquot_sum THEN 'abundant' + WHEN number > aliquot_sum THEN 'deficient' + END + FROM aliquot_sums + ) + WHERE error ISNULL + AND result ISNULL +; diff --git a/exercises/practice/perfect-numbers/.meta/tests.toml b/exercises/practice/perfect-numbers/.meta/tests.toml new file mode 100644 index 0000000..ec28889 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/tests.toml @@ -0,0 +1,49 @@ +# 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. + +[163e8e86-7bfd-4ee2-bd68-d083dc3381a3] +description = "Perfect numbers -> Smallest perfect number is classified correctly" + +[169a7854-0431-4ae0-9815-c3b6d967436d] +description = "Perfect numbers -> Medium perfect number is classified correctly" + +[ee3627c4-7b36-4245-ba7c-8727d585f402] +description = "Perfect numbers -> Large perfect number is classified correctly" + +[80ef7cf8-9ea8-49b9-8b2d-d9cb3db3ed7e] +description = "Abundant numbers -> Smallest abundant number is classified correctly" + +[3e300e0d-1a12-4f11-8c48-d1027165ab60] +description = "Abundant numbers -> Medium abundant number is classified correctly" + +[ec7792e6-8786-449c-b005-ce6dd89a772b] +description = "Abundant numbers -> Large abundant number is classified correctly" + +[e610fdc7-2b6e-43c3-a51c-b70fb37413ba] +description = "Deficient numbers -> Smallest prime deficient number is classified correctly" + +[0beb7f66-753a-443f-8075-ad7fbd9018f3] +description = "Deficient numbers -> Smallest non-prime deficient number is classified correctly" + +[1c802e45-b4c6-4962-93d7-1cad245821ef] +description = "Deficient numbers -> Medium deficient number is classified correctly" + +[47dd569f-9e5a-4a11-9a47-a4e91c8c28aa] +description = "Deficient numbers -> Large deficient number is classified correctly" + +[a696dec8-6147-4d68-afad-d38de5476a56] +description = "Deficient numbers -> Edge case (no factors other than itself) is classified correctly" + +[72445cee-660c-4d75-8506-6c40089dc302] +description = "Invalid inputs -> Zero is rejected (as it is not a positive integer)" + +[2d72ce2c-6802-49ac-8ece-c790ba3dae13] +description = "Invalid inputs -> Negative integer is rejected (as it is not a positive integer)" diff --git a/exercises/practice/perfect-numbers/create_fixture.sql b/exercises/practice/perfect-numbers/create_fixture.sql new file mode 100644 index 0000000..3df0c12 --- /dev/null +++ b/exercises/practice/perfect-numbers/create_fixture.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS "perfect-numbers"; +CREATE TABLE "perfect-numbers" ( + number INTEGER NOT NULL, + result TEXT, + error TEXT +); + +.mode csv +.import ./data.csv "perfect-numbers" + +UPDATE "perfect-numbers" SET result = NULL, error = NULL; diff --git a/exercises/practice/perfect-numbers/create_test_table.sql b/exercises/practice/perfect-numbers/create_test_table.sql new file mode 100644 index 0000000..ab8fb53 --- /dev/null +++ b/exercises/practice/perfect-numbers/create_test_table.sql @@ -0,0 +1,32 @@ +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 + number INTEGER NOT NULL, + expected_result TEXT, + expected_error TEXT +); + +INSERT INTO tests (uuid, description, number, expected_result, expected_error) +VALUES + ('163e8e86-7bfd-4ee2-bd68-d083dc3381a3', 'Smallest perfect number is classified correctly', 6, 'perfect', NULL), + ('169a7854-0431-4ae0-9815-c3b6d967436d', 'Medium perfect number is classified correctly', 28, 'perfect', NULL), + ('ee3627c4-7b36-4245-ba7c-8727d585f402', 'Large perfect number is classified correctly', 33550336, 'perfect', NULL), + ('80ef7cf8-9ea8-49b9-8b2d-d9cb3db3ed7e', 'Smallest abundant number is classified correctly', 12, 'abundant', NULL), + ('3e300e0d-1a12-4f11-8c48-d1027165ab60', 'Medium abundant number is classified correctly', 30, 'abundant', NULL), + ('ec7792e6-8786-449c-b005-ce6dd89a772b', 'Large abundant number is classified correctly', 33550335, 'abundant', NULL), + ('e610fdc7-2b6e-43c3-a51c-b70fb37413ba', 'Smallest prime deficient number is classified correctly', 2, 'deficient', NULL), + ('0beb7f66-753a-443f-8075-ad7fbd9018f3', 'Smallest non-prime deficient number is classified correctly', 4, 'deficient', NULL), + ('1c802e45-b4c6-4962-93d7-1cad245821ef', 'Medium deficient number is classified correctly', 32, 'deficient', NULL), + ('47dd569f-9e5a-4a11-9a47-a4e91c8c28aa', 'Large deficient number is classified correctly', 33550337, 'deficient', NULL), + ('a696dec8-6147-4d68-afad-d38de5476a56', 'Edge case (no factors other than itself) is classified correctly', 1, 'deficient', NULL), + ('72445cee-660c-4d75-8506-6c40089dc302', 'Zero is rejected (as it is not a positive integer)', 0, NULL, 'Classification is only possible for positive integers.'), + ('2d72ce2c-6802-49ac-8ece-c790ba3dae13', 'Negative integer is rejected (as it is not a positive integer)', -1, NULL, 'Classification is only possible for positive integers.'); diff --git a/exercises/practice/perfect-numbers/data.csv b/exercises/practice/perfect-numbers/data.csv new file mode 100644 index 0000000..d6b8ad4 --- /dev/null +++ b/exercises/practice/perfect-numbers/data.csv @@ -0,0 +1,13 @@ +6,, +28,, +33550336,, +12,, +30,, +33550335,, +2,, +4,, +32,, +33550337,, +1,, +0,, +-1,, diff --git a/exercises/practice/perfect-numbers/perfect-numbers.sql b/exercises/practice/perfect-numbers/perfect-numbers.sql new file mode 100644 index 0000000..27cfc38 --- /dev/null +++ b/exercises/practice/perfect-numbers/perfect-numbers.sql @@ -0,0 +1,8 @@ +-- Schema: +-- CREATE TABLE "perfect-numbers" ( +-- number INTEGER NOT NULL, +-- result TEXT, +-- error TEXT +-- ); +-- +-- Task: update the perfect-numbers table and set the result or the error columns based on number. diff --git a/exercises/practice/perfect-numbers/perfect-numbers_test.sql b/exercises/practice/perfect-numbers/perfect-numbers_test.sql new file mode 100644 index 0000000..9b7b765 --- /dev/null +++ b/exercises/practice/perfect-numbers/perfect-numbers_test.sql @@ -0,0 +1,48 @@ +-- 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 ./perfect-numbers.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 number, result, error FROM "perfect-numbers") AS actual + WHERE actual.number = tests.NUMBER + AND (actual.result = tests.expected_result + OR COALESCE(actual.result, tests.expected_result) ISNULL) + AND (actual.error = tests.expected_error + OR COALESCE(actual.error, tests.expected_error) ISNULL); + +-- Update message for failed tests to give helpful information: +UPDATE tests + SET message = ( + 'Result for "' || actual.number || '"' || ' is <' + || PRINTF('result=%s and error=%s', + COALESCE(actual.result, 'NULL'), + COALESCE(actual.error, 'NULL')) + || '> but should be <' + || PRINTF('result=%s and error=%s', + COALESCE(tests.expected_result, 'NULL'), + COALESCE(tests.expected_error, 'NULL')) + || '>' + ) + FROM (SELECT number, result, error FROM "perfect-numbers") 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 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;