Skip to content

Commit 6e9f54b

Browse files
authored
Add perfect numbers (#55)
1 parent bc8d4c3 commit 6e9f54b

File tree

15 files changed

+576
-0
lines changed

15 files changed

+576
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,14 @@
289289
"prerequisites": [],
290290
"difficulty": 4
291291
},
292+
{
293+
"slug": "perfect-numbers",
294+
"name": "Perfect Numbers",
295+
"uuid": "1e2d1960-e71d-48b7-8e65-cc9498daa713",
296+
"practices": [],
297+
"prerequisites": [],
298+
"difficulty": 4
299+
},
292300
{
293301
"slug": "prime-factors",
294302
"name": "Prime Factors",
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Instructions append
2+
3+
## Defining an inductive type
4+
5+
This exercise expects the return value to be an [inductive type][inductive] `Classification` that you must define.
6+
7+
This type must have a [BEq][beq] instance, i.e., it admits a Boolean equality test, and a [Repr][repr] instance, i.e., values of the type can be formatted for display.
8+
9+
You can refer to [this page][instance] on instance declarations.
10+
Lean can automatically [generate instances][deriving] for many classes.
11+
12+
## Subtypes
13+
14+
The argument to the `classify` function is a [subtype][subtype] called `Positive`:
15+
16+
```
17+
def Positive := { x : Nat // x > 0 }
18+
```
19+
20+
Subtypes represent elements of a particular type that satisfy a predicate.
21+
`Positive` represents elements of `Nat` that are greater than `0`.
22+
23+
Any element of a subtype can be thought of as a pair consisting of a value of the underlying type and a proof, in the mathematical sense, that this value satisfies the corresponding predicate.
24+
25+
Subtypes enforce invariants at the type level,ensuring that ill-formed values cannot be constructed.
26+
27+
[inductive]: https://lean-lang.org/functional_programming_in_lean/Getting-to-Know-Lean/Datatypes-and-Patterns/#datatypes-and-patterns
28+
[beq]: https://lean-lang.org/doc/reference/latest/Type-Classes/Basic-Classes/#BEq___mk
29+
[repr]: https://lean-lang.org/doc/reference/4.26.0/Interacting-with-Lean/?terms=Repr#repr
30+
[instance]: https://lean-lang.org/doc/reference/latest/Type-Classes/Deriving-Instances/#deriving-instances
31+
[deriving]: https://lean-lang.org/doc/reference/latest/Type-Classes/Deriving-Instances/#deriving-instances
32+
[subtype]: https://lean-lang.org/doc/reference/latest/Basic-Types/Subtypes/#Subtype___mk
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Instructions
2+
3+
Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers.
4+
5+
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].
6+
The _aliquot sum_ is defined as the sum of the factors of a number not including the number itself.
7+
For example, the aliquot sum of `15` is `1 + 3 + 5 = 9`.
8+
9+
## Perfect
10+
11+
A number is perfect when it equals its aliquot sum.
12+
For example:
13+
14+
- `6` is a perfect number because `1 + 2 + 3 = 6`
15+
- `28` is a perfect number because `1 + 2 + 4 + 7 + 14 = 28`
16+
17+
## Abundant
18+
19+
A number is abundant when it is less than its aliquot sum.
20+
For example:
21+
22+
- `12` is an abundant number because `1 + 2 + 3 + 4 + 6 = 16`
23+
- `24` is an abundant number because `1 + 2 + 3 + 4 + 6 + 8 + 12 = 36`
24+
25+
## Deficient
26+
27+
A number is deficient when it is greater than its aliquot sum.
28+
For example:
29+
30+
- `8` is a deficient number because `1 + 2 + 4 = 7`
31+
- Prime numbers are deficient
32+
33+
## Task
34+
35+
Implement a way to determine whether a given number is [perfect](#perfect).
36+
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).
37+
38+
[nicomachus]: https://en.wikipedia.org/wiki/Nicomachus
39+
[aliquot-sum]: https://en.wikipedia.org/wiki/Aliquot_sum
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace PerfectNumbers
2+
3+
def Positive := { x : Nat // x > 0 }
4+
5+
inductive Classification where
6+
| abundant : Classification
7+
| perfect : Classification
8+
| deficient : Classification
9+
deriving BEq, Repr
10+
11+
def getFactors (number : Nat) (factor : Nat) (acc : List Nat) : List Nat :=
12+
if factor < number then
13+
if number % factor == 0 then
14+
getFactors number (factor + 1) (factor :: acc)
15+
else
16+
getFactors number (factor + 1) acc
17+
else acc
18+
19+
def classify (number : Positive) : Classification :=
20+
let factors := getFactors number.val 1 []
21+
let sum := factors.foldl (· + ·) 0
22+
match compare sum number.val with
23+
| .lt => .deficient
24+
| .gt => .abundant
25+
| .eq => .perfect
26+
27+
end PerfectNumbers
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"oxe-i"
4+
],
5+
"files": {
6+
"solution": [
7+
"PerfectNumbers.lean"
8+
],
9+
"test": [
10+
"PerfectNumbersTest.lean"
11+
],
12+
"example": [
13+
".meta/Example.lean"
14+
]
15+
},
16+
"blurb": "Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers.",
17+
"source": "Taken from Chapter 2 of Functional Thinking by Neal Ford.",
18+
"source_url": "https://www.oreilly.com/library/view/functional-thinking/9781449365509/"
19+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[163e8e86-7bfd-4ee2-bd68-d083dc3381a3]
13+
description = "Perfect numbers -> Smallest perfect number is classified correctly"
14+
15+
[169a7854-0431-4ae0-9815-c3b6d967436d]
16+
description = "Perfect numbers -> Medium perfect number is classified correctly"
17+
18+
[ee3627c4-7b36-4245-ba7c-8727d585f402]
19+
description = "Perfect numbers -> Large perfect number is classified correctly"
20+
21+
[80ef7cf8-9ea8-49b9-8b2d-d9cb3db3ed7e]
22+
description = "Abundant numbers -> Smallest abundant number is classified correctly"
23+
24+
[3e300e0d-1a12-4f11-8c48-d1027165ab60]
25+
description = "Abundant numbers -> Medium abundant number is classified correctly"
26+
27+
[ec7792e6-8786-449c-b005-ce6dd89a772b]
28+
description = "Abundant numbers -> Large abundant number is classified correctly"
29+
30+
[e610fdc7-2b6e-43c3-a51c-b70fb37413ba]
31+
description = "Deficient numbers -> Smallest prime deficient number is classified correctly"
32+
33+
[0beb7f66-753a-443f-8075-ad7fbd9018f3]
34+
description = "Deficient numbers -> Smallest non-prime deficient number is classified correctly"
35+
36+
[1c802e45-b4c6-4962-93d7-1cad245821ef]
37+
description = "Deficient numbers -> Medium deficient number is classified correctly"
38+
39+
[47dd569f-9e5a-4a11-9a47-a4e91c8c28aa]
40+
description = "Deficient numbers -> Large deficient number is classified correctly"
41+
42+
[a696dec8-6147-4d68-afad-d38de5476a56]
43+
description = "Deficient numbers -> Edge case (no factors other than itself) is classified correctly"
44+
45+
[72445cee-660c-4d75-8506-6c40089dc302]
46+
description = "Invalid inputs -> Zero is rejected (as it is not a positive integer)"
47+
include = false
48+
49+
[2d72ce2c-6802-49ac-8ece-c790ba3dae13]
50+
description = "Invalid inputs -> Negative integer is rejected (as it is not a positive integer)"
51+
include = false
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace PerfectNumbers
2+
3+
def Positive := { x : Nat // x > 0 }
4+
5+
/-
6+
You should define Classification here
7+
-/
8+
9+
def classify (number : Positive) : Classification :=
10+
sorry
11+
12+
end PerfectNumbers
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import LeanTest
2+
import PerfectNumbers
3+
4+
open LeanTest
5+
6+
def perfectNumbersTests : TestSuite :=
7+
(TestSuite.empty "PerfectNumbers")
8+
|>.addTest "Perfect numbers -> Smallest perfect number is classified correctly" (do
9+
return assertEqual PerfectNumbers.Classification.perfect (PerfectNumbers.classify ⟨6, (by decide)⟩))
10+
|>.addTest "Perfect numbers -> Medium perfect number is classified correctly" (do
11+
return assertEqual PerfectNumbers.Classification.perfect (PerfectNumbers.classify ⟨28, (by decide)⟩))
12+
|>.addTest "Perfect numbers -> Large perfect number is classified correctly" (do
13+
return assertEqual PerfectNumbers.Classification.perfect (PerfectNumbers.classify ⟨33550336, (by decide)⟩))
14+
|>.addTest "Abundant numbers -> Smallest abundant number is classified correctly" (do
15+
return assertEqual PerfectNumbers.Classification.abundant (PerfectNumbers.classify ⟨12, (by decide)⟩))
16+
|>.addTest "Abundant numbers -> Medium abundant number is classified correctly" (do
17+
return assertEqual PerfectNumbers.Classification.abundant (PerfectNumbers.classify ⟨30, (by decide)⟩))
18+
|>.addTest "Abundant numbers -> Large abundant number is classified correctly" (do
19+
return assertEqual PerfectNumbers.Classification.abundant (PerfectNumbers.classify ⟨33550335, (by decide)⟩))
20+
|>.addTest "Deficient numbers -> Smallest prime deficient number is classified correctly" (do
21+
return assertEqual PerfectNumbers.Classification.deficient (PerfectNumbers.classify ⟨2, (by decide)⟩))
22+
|>.addTest "Deficient numbers -> Smallest non-prime deficient number is classified correctly" (do
23+
return assertEqual PerfectNumbers.Classification.deficient (PerfectNumbers.classify ⟨4, (by decide)⟩))
24+
|>.addTest "Deficient numbers -> Medium deficient number is classified correctly" (do
25+
return assertEqual PerfectNumbers.Classification.deficient (PerfectNumbers.classify ⟨32, (by decide)⟩))
26+
|>.addTest "Deficient numbers -> Large deficient number is classified correctly" (do
27+
return assertEqual PerfectNumbers.Classification.deficient (PerfectNumbers.classify ⟨33550337, (by decide)⟩))
28+
|>.addTest "Deficient numbers -> Edge case (no factors other than itself) is classified correctly" (do
29+
return assertEqual PerfectNumbers.Classification.deficient (PerfectNumbers.classify ⟨1, (by decide)⟩))
30+
31+
def main : IO UInt32 := do
32+
runTestSuitesWithExitCode [perfectNumbersTests]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name = "perfect-numbers"
2+
version = "0.1.0"
3+
defaultTargets = ["PerfectNumbersTest"]
4+
testDriver = "PerfectNumbersTest"
5+
6+
[[lean_lib]]
7+
name = "LeanTest"
8+
srcDir = "vendor/LeanTest"
9+
10+
[[lean_lib]]
11+
name = "PerfectNumbers"
12+
13+
[[lean_exe]]
14+
name = "PerfectNumbersTest"
15+
root = "PerfectNumbersTest"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
leanprover/lean4:v4.25.2

0 commit comments

Comments
 (0)