From dae8f8616655d636292c6d9ea2daca411dbc455c Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Thu, 3 Aug 2023 20:50:19 +0200 Subject: [PATCH] exercises(perfect-numbers): implement (#184) --- config.json | 18 +++++ .../.docs/instructions.append.md | 13 ++++ .../perfect-numbers/.docs/instructions.md | 24 +++++++ .../perfect-numbers/.meta/config.json | 19 +++++ .../perfect-numbers/.meta/example.zig | 32 +++++++++ .../practice/perfect-numbers/.meta/tests.toml | 51 +++++++++++++ .../perfect-numbers/perfect_numbers.zig | 11 +++ .../perfect-numbers/test_perfect_numbers.zig | 72 +++++++++++++++++++ 8 files changed, 240 insertions(+) create mode 100644 exercises/practice/perfect-numbers/.docs/instructions.append.md create mode 100644 exercises/practice/perfect-numbers/.docs/instructions.md create mode 100644 exercises/practice/perfect-numbers/.meta/config.json create mode 100644 exercises/practice/perfect-numbers/.meta/example.zig create mode 100644 exercises/practice/perfect-numbers/.meta/tests.toml create mode 100644 exercises/practice/perfect-numbers/perfect_numbers.zig create mode 100644 exercises/practice/perfect-numbers/test_perfect_numbers.zig diff --git a/config.json b/config.json index 31e19eaa..f8252738 100644 --- a/config.json +++ b/config.json @@ -379,6 +379,24 @@ ], "difficulty": 1 }, + { + "uuid": "2fbd5bd8-a839-42a3-9293-4ad577766a12", + "slug": "perfect-numbers", + "name": "Perfect Numbers", + "practices": [ + "asserting", + "enums", + "integers", + "math" + ], + "prerequisites": [ + "asserting", + "enums", + "integers", + "math" + ], + "difficulty": 1 + }, { "uuid": "866a09d8-6949-4363-b83f-7b9fefb53893", "slug": "two-fer", diff --git a/exercises/practice/perfect-numbers/.docs/instructions.append.md b/exercises/practice/perfect-numbers/.docs/instructions.append.md new file mode 100644 index 00000000..609ccddd --- /dev/null +++ b/exercises/practice/perfect-numbers/.docs/instructions.append.md @@ -0,0 +1,13 @@ +# Instructions append + +## Assert + +For this exercise, let's say that we want the caller to be responsible for calling `classify` only with a nonzero input. +So please make the `classify` function assert that its input is nonzero. +For more details, see the [Zig Language Reference][zig-reference] and the implementation of [`std.debug.assert`][assert]. + +However, note that this exercise does not currently test an input of 0 (because `std.testing` does [not yet support expecting a panic][proposal]). + +[zig-reference]: https://ziglang.org/documentation/0.10.1/#unreachable +[assert]: https://github.com/ziglang/zig/blob/0.10.1/lib/std/debug.zig#L267-L279 +[proposal]: https://github.com/ziglang/zig/issues/1356 diff --git a/exercises/practice/perfect-numbers/.docs/instructions.md b/exercises/practice/perfect-numbers/.docs/instructions.md new file mode 100644 index 00000000..0dae8867 --- /dev/null +++ b/exercises/practice/perfect-numbers/.docs/instructions.md @@ -0,0 +1,24 @@ +# 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**, **abundant**, or **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**: aliquot sum = number + - 6 is a perfect number because (1 + 2 + 3) = 6 + - 28 is a perfect number because (1 + 2 + 4 + 7 + 14) = 28 +- **Abundant**: aliquot sum > number + - 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**: aliquot sum < number + - 8 is a deficient number because (1 + 2 + 4) = 7 + - Prime numbers are deficient + +Implement a way to determine whether a given number is **perfect**. +Depending on your language track, you may also need to implement a way to determine whether a given number is **abundant** or **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 00000000..8edf3ef2 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "ee7" + ], + "files": { + "solution": [ + "perfect_numbers.zig" + ], + "test": [ + "test_perfect_numbers.zig" + ], + "example": [ + ".meta/example.zig" + ] + }, + "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.zig b/exercises/practice/perfect-numbers/.meta/example.zig new file mode 100644 index 00000000..a4d6a845 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/example.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +pub const Classification = enum { + deficient, + perfect, + abundant, +}; + +/// Returns the sum of the divisors of `n` (excluding `n` itself). +/// For example, the aliquot sum of 15 is (1 + 3 + 5) = 9. +fn aliquotSum(n: u64) u64 { + if (n == 1) return 0; + var result: u64 = 1; + var i: usize = 2; + const isqrt_n = std.math.sqrt(n); + while (i <= isqrt_n) : (i += 1) { + if (n % i == 0) result += i + (n / i); + } + // When `n` is a square number, we added the same divisor twice inside the loop. + if (isqrt_n * isqrt_n == n) result -= isqrt_n; + return result; +} + +/// Returns whether `n` is less than, equal to, or greater than its aliquot sum. +/// Asserts that `n` is nonzero. +pub fn classify(comptime n: u64) Classification { + comptime std.debug.assert(n != 0); + const aliquot_sum = aliquotSum(n); + if (aliquot_sum < n) return Classification.deficient; + if (aliquot_sum > n) return Classification.abundant; + return Classification.perfect; +} diff --git a/exercises/practice/perfect-numbers/.meta/tests.toml b/exercises/practice/perfect-numbers/.meta/tests.toml new file mode 100644 index 00000000..5cf3b7a5 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/tests.toml @@ -0,0 +1,51 @@ +# 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)" +include = false + +[2d72ce2c-6802-49ac-8ece-c790ba3dae13] +description = "Invalid inputs -> Negative integer is rejected (as it is not a positive integer)" +include = false diff --git a/exercises/practice/perfect-numbers/perfect_numbers.zig b/exercises/practice/perfect-numbers/perfect_numbers.zig new file mode 100644 index 00000000..df8c02e7 --- /dev/null +++ b/exercises/practice/perfect-numbers/perfect_numbers.zig @@ -0,0 +1,11 @@ +pub const Classification = enum { + deficient, + perfect, + abundant, +}; + +/// Asserts that `n` is nonzero. +pub fn classify(n: u64) Classification { + _ = n; + @compileError("please implement the classify function"); +} diff --git a/exercises/practice/perfect-numbers/test_perfect_numbers.zig b/exercises/practice/perfect-numbers/test_perfect_numbers.zig new file mode 100644 index 00000000..fe0afc8b --- /dev/null +++ b/exercises/practice/perfect-numbers/test_perfect_numbers.zig @@ -0,0 +1,72 @@ +const std = @import("std"); +const testing = std.testing; + +const perfect_numbers = @import("perfect_numbers.zig"); +const Classification = perfect_numbers.Classification; +const classify = perfect_numbers.classify; + +test "smallest perfect number is classified correctly" { + const expected = Classification.perfect; + const actual = classify(6); + try testing.expectEqual(expected, actual); +} + +test "medium perfect number is classified correctly" { + const expected = Classification.perfect; + const actual = classify(28); + try testing.expectEqual(expected, actual); +} + +test "large perfect number is classified correctly" { + const expected = Classification.perfect; + const actual = classify(33_550_336); + try testing.expectEqual(expected, actual); +} + +test "smallest abundant number is classified correctly" { + const expected = Classification.abundant; + const actual = classify(12); + try testing.expectEqual(expected, actual); +} + +test "medium abundant number is classified correctly" { + const expected = Classification.abundant; + const actual = classify(30); + try testing.expectEqual(expected, actual); +} + +test "large abundant number is classified correctly" { + const expected = Classification.abundant; + const actual = classify(33_550_335); + try testing.expectEqual(expected, actual); +} + +test "smallest prime deficient number is classified correctly" { + const expected = Classification.deficient; + const actual = classify(2); + try testing.expectEqual(expected, actual); +} + +test "smallest non-prime deficient number is classified correctly" { + const expected = Classification.deficient; + const actual = classify(4); + try testing.expectEqual(expected, actual); +} + +test "medium deficient number is classified correctly" { + const expected = Classification.deficient; + const actual = classify(32); + try testing.expectEqual(expected, actual); +} + +test "large deficient number is classified correctly" { + const expected = Classification.deficient; + const actual = classify(33_550_337); + try testing.expectEqual(expected, actual); +} + +test "edge case (no factors other than itself) is classified correctly" { + const expected = Classification.deficient; + const actual = classify(1); + try testing.expectEqual(expected, actual); +}