Skip to content

Commit

Permalink
exercises(perfect-numbers): implement (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
ee7 authored Aug 3, 2023
1 parent eec393e commit dae8f86
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 0 deletions.
18 changes: 18 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 13 additions & 0 deletions exercises/practice/perfect-numbers/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions exercises/practice/perfect-numbers/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions exercises/practice/perfect-numbers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -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/"
}
32 changes: 32 additions & 0 deletions exercises/practice/perfect-numbers/.meta/example.zig
Original file line number Diff line number Diff line change
@@ -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;
}
51 changes: 51 additions & 0 deletions exercises/practice/perfect-numbers/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions exercises/practice/perfect-numbers/perfect_numbers.zig
Original file line number Diff line number Diff line change
@@ -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");
}
72 changes: 72 additions & 0 deletions exercises/practice/perfect-numbers/test_perfect_numbers.zig
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit dae8f86

Please sign in to comment.