Skip to content

Commit

Permalink
Add perfect-numbers exercise (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
keiravillekode authored Nov 10, 2024
1 parent 5cacf54 commit b82516f
Show file tree
Hide file tree
Showing 12 changed files with 3,663 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,14 @@
"prerequisites": [],
"difficulty": 4
},
{
"slug": "perfect-numbers",
"name": "Perfect Numbers",
"uuid": "f3a67356-dca2-4cd6-87dc-8e7558d31381",
"practices": [],
"prerequisites": [],
"difficulty": 4
},
{
"slug": "phone-number",
"name": "Phone Number",
Expand Down
39 changes: 39 additions & 0 deletions exercises/practice/perfect-numbers/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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
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": [
"keiravillekode"
],
"files": {
"solution": [
"perfect_numbers.s"
],
"test": [
"perfect_numbers_test.c"
],
"example": [
".meta/example.s"
]
},
"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/"
}
62 changes: 62 additions & 0 deletions exercises/practice/perfect-numbers/.meta/example.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.equ DEFICIENT, 1
.equ PERFECT, 2
.equ ABUNDANT, 3
.equ INVALID, -1

.text
.globl classify

/* extern int classify(int64_t number); */
classify:
cmp x0, #1
ble .le_one

mov x1, #1 /* product of factors */
mov x2, #1
lsl x4, x0, #1 /* 2 * number */

.next:
add x2, x2, #1 /* candidate factor */
mov x5, #1 /* sum of powers of factor */
mul x3, x2, x2
cmp x3, x0
csel x2, x0, x2, hi /* if x2*x2 exceeds x0, then set x2 to x0 */

udiv x6, x0, x2 /* quotient */
msub x7, x6, x2, x0 /* remainder */
cbnz x7, .next

.repeat:
mul x5, x5, x2
add x5, x5, #1
mov x0, x6

udiv x6, x0, x2 /* quotient */
msub x7, x6, x2, x0 /* remainder */
cbz x7, .repeat

mul x1, x1, x5
cmp x0, #1
bne .next

cmp x1, x4
beq .perfect
blo .deficient

.abundant:
mov x0, ABUNDANT
ret

.perfect:
mov x0, PERFECT
ret

.deficient:
mov x0, DEFICIENT
ret

.le_one:
beq .deficient

mov x0, INVALID
ret
49 changes: 49 additions & 0 deletions exercises/practice/perfect-numbers/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -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)"
36 changes: 36 additions & 0 deletions exercises/practice/perfect-numbers/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
AS = aarch64-linux-gnu-as
CC = aarch64-linux-gnu-gcc

CFLAGS = -g -Wall -Wextra -pedantic -Werror
LDFLAGS =

ALL_LDFLAGS = -pie -Wl,--fatal-warnings

ALL_CFLAGS = -std=c99 -fPIE $(CFLAGS)
ALL_LDFLAGS += $(LDFLAGS)

C_OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
AS_OBJS = $(patsubst %.s,%.o,$(wildcard *.s))
ALL_OBJS = $(filter-out example.o,$(C_OBJS) $(AS_OBJS) vendor/unity.o)

CC_CMD = $(CC) $(ALL_CFLAGS) -c -o $@ $<

all: tests
qemu-aarch64 -L /usr/aarch64-linux-gnu ./$<

tests: $(ALL_OBJS)
@$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $(ALL_OBJS)

%.o: %.s
@$(AS) -o $@ $<

%.o: %.c
@$(CC_CMD)

vendor/unity.o: vendor/unity.c vendor/unity.h vendor/unity_internals.h
@$(CC_CMD)

clean:
@rm -f *.o vendor/*.o tests

.PHONY: all clean
10 changes: 10 additions & 0 deletions exercises/practice/perfect-numbers/perfect_numbers.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.equ DEFICIENT, 1
.equ PERFECT, 2
.equ ABUNDANT, 3
.equ INVALID, -1

.text
.globl classify

classify:
ret
104 changes: 104 additions & 0 deletions exercises/practice/perfect-numbers/perfect_numbers_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "vendor/unity.h"

#include <stdint.h>

#define DEFICIENT 1
#define PERFECT 2
#define ABUNDANT 3
#define INVALID -1

extern int classify(int64_t number);

void setUp(void) {
}

void tearDown(void) {
}

void test_smallest_perfect_number_is_classified_correctly(void) {
TEST_ASSERT_EQUAL_INT(PERFECT, classify(6));
}

void test_medium_perfect_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(PERFECT, classify(28));
}

void test_large_perfect_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(PERFECT, classify(33550336));
}

void test_smallest_abundant_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(ABUNDANT, classify(12));
}

void test_medium_abundant_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(ABUNDANT, classify(30));
}

void test_large_abundant_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(ABUNDANT, classify(33550335));
}

void test_smallest_prime_deficient_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(DEFICIENT, classify(2));
}

void test_smallest_nonprime_deficient_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(DEFICIENT, classify(4));
}

void test_medium_deficient_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(DEFICIENT, classify(32));
}

void test_large_deficient_number_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(DEFICIENT, classify(33550337));
}

void test_edge_case_no_factors_other_than_itself_is_classified_correctly(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(DEFICIENT, classify(1));
}

void test_zero_is_rejected_as_it_is_not_a_positive_integer(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(INVALID, classify(0));
}

void test_negative_integer_is_rejected_as_it_is_not_a_positive_integer(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(INVALID, classify(-1));
}

void test_large_negative_is_rejected(void) {
TEST_IGNORE();
TEST_ASSERT_EQUAL_INT(INVALID, classify(-7001002003));
}

int main(void) {
UNITY_BEGIN();
RUN_TEST(test_smallest_perfect_number_is_classified_correctly);
RUN_TEST(test_medium_perfect_number_is_classified_correctly);
RUN_TEST(test_large_perfect_number_is_classified_correctly);
RUN_TEST(test_smallest_abundant_number_is_classified_correctly);
RUN_TEST(test_medium_abundant_number_is_classified_correctly);
RUN_TEST(test_large_abundant_number_is_classified_correctly);
RUN_TEST(test_smallest_prime_deficient_number_is_classified_correctly);
RUN_TEST(test_smallest_nonprime_deficient_number_is_classified_correctly);
RUN_TEST(test_medium_deficient_number_is_classified_correctly);
RUN_TEST(test_large_deficient_number_is_classified_correctly);
RUN_TEST(test_edge_case_no_factors_other_than_itself_is_classified_correctly);
RUN_TEST(test_zero_is_rejected_as_it_is_not_a_positive_integer);
RUN_TEST(test_negative_integer_is_rejected_as_it_is_not_a_positive_integer);
RUN_TEST(test_large_negative_is_rejected);
return UNITY_END();
}
Loading

0 comments on commit b82516f

Please sign in to comment.