Skip to content

Commit

Permalink
Add perfect-numbers (#223)
Browse files Browse the repository at this point in the history
  • Loading branch information
BNAndras authored Jan 3, 2024
1 parent da15319 commit 166432f
Show file tree
Hide file tree
Showing 11 changed files with 404 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@
"prerequisites": [],
"difficulty": 1
},
{
"slug": "perfect-numbers",
"name": "Perfect Numbers",
"uuid": "56535bf0-960c-4f45-a97e-fb35e237ed9b",
"practices": [],
"prerequisites": [],
"difficulty": 2
},
{
"slug": "pig-latin",
"name": "Pig Latin",
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
38 changes: 38 additions & 0 deletions exercises/practice/perfect-numbers/.meta/Example.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Here is an example solution for the PerfectNumbers exercise
*/
component {

/**
* @returns
*/
function classify( number ) {
if (number < 1 ) {
throw 'Classification is only possible for positive integers.';
}

if (number == 1) {
return 'deficient';
}

var aliquotSum = 1
for (i = 2; i <= sqr(number); i++) {
if (number % i == 0) {
var increment = i;
if (i ^ 2 != number) {
increment += number / i;
}
aliquotSum += increment;
}
}

if (aliquotSum < number) {
return 'deficient';
} elseif (aliquotSum > number) {
return 'abundant';
} else {
return 'perfect';
}
}

}
7 changes: 7 additions & 0 deletions exercises/practice/perfect-numbers/.meta/ExampleTest.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
component extends="PerfectNumbersTest" {

function beforeAll(){
SUT = createObject( 'Solution' );
}

}
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": [
"BNAndras"
],
"files": {
"solution": [
"PerfectNumbers.cfc"
],
"test": [
"PerfectNumbersTest.cfc"
],
"example": [
".meta/Example.cfc"
]
},
"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/"
}
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)"
13 changes: 13 additions & 0 deletions exercises/practice/perfect-numbers/PerfectNumbers.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Your implementation of the PerfectNumbers exercise
*/
component {

/**
* @returns
*/
function classify( number ) {
// Implement me here
}

}
83 changes: 83 additions & 0 deletions exercises/practice/perfect-numbers/PerfectNumbersTest.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
component extends="testbox.system.BaseSpec" {

function beforeAll(){
SUT = createObject( 'PerfectNumbers' );
}

function run(){

describe( "My PerfectNumbers class", function(){

describe( 'Perfect numbers', function(){

it( 'Smallest perfect number is classified correctly', function(){
expect( SUT.classify( number='6' ) ).toBe( 'perfect' );
});

it( 'Medium perfect number is classified correctly', function(){
expect( SUT.classify( number='28' ) ).toBe( 'perfect' );
});

it( 'Large perfect number is classified correctly', function(){
expect( SUT.classify( number='33550336' ) ).toBe( 'perfect' );
});

});

describe( 'Abundant numbers', function(){

it( 'Smallest abundant number is classified correctly', function(){
expect( SUT.classify( number='12' ) ).toBe( 'abundant' );
});

it( 'Medium abundant number is classified correctly', function(){
expect( SUT.classify( number='30' ) ).toBe( 'abundant' );
});

it( 'Large abundant number is classified correctly', function(){
expect( SUT.classify( number='33550335' ) ).toBe( 'abundant' );
});

});

describe( 'Deficient numbers', function(){

it( 'Smallest prime deficient number is classified correctly', function(){
expect( SUT.classify( number='2' ) ).toBe( 'deficient' );
});

it( 'Smallest non-prime deficient number is classified correctly', function(){
expect( SUT.classify( number='4' ) ).toBe( 'deficient' );
});

it( 'Medium deficient number is classified correctly', function(){
expect( SUT.classify( number='32' ) ).toBe( 'deficient' );
});

it( 'Large deficient number is classified correctly', function(){
expect( SUT.classify( number='33550337' ) ).toBe( 'deficient' );
});

it( 'Edge case (no factors other than itself) is classified correctly', function(){
expect( SUT.classify( number='1' ) ).toBe( 'deficient' );
});

});

describe( 'Invalid inputs', function(){

it( 'Zero is rejected (as it is not a positive integer)', function(){
expect( function(){ SUT.classify( number='0' ); } ).toThrow( message='Classification is only possible for positive integers.' );
});

it( 'Negative integer is rejected (as it is not a positive integer)', function(){
expect( function(){ SUT.classify( number='-1' ); } ).toThrow( message='Classification is only possible for positive integers.' );
});

});

});

}

}
103 changes: 103 additions & 0 deletions exercises/practice/perfect-numbers/TestRunner.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* I am a CommandBox task runner which you can use to test your implementation of this exercise against the
* provided test suite. To use me, open the CommandBox CLI and run this:
*
* CommandBox> task run TestRunner
*
* To start up a test watcher that will automatically rerun the test suite every time you save a file change, run this:
*
* CommandBox> task run TestRunner --watcher
*
*/
component {

/**
* @solution Runs the tests against the solution
* @watcher Start up a file watch that re-runs the tests on file changes. Use Ctrl-C to stop
*/
function run( boolean solution=false, boolean watcher=false ) {

ensureTestBox();

if( watcher ) {

// Tabula rasa
command( 'cls' ).run();

// Start watcher
watch()
.paths( '*.cfc' )
.inDirectory( getCWD() )
.withDelay( 500 )
.onChange( function() {

// Clear the screen
command( 'cls' )
.run();

// This is neccessary so changes to tests get picked up right away.
pagePoolClear();

runTests( solution );

} )
.start();

} else {
runTests( solution );
}

}

/**
* Make sure the TestBox framework is installed
*/
private function ensureTestBox() {
var excerciseRoot = getCWD();
var testBoxRoot = excerciseRoot & '/testbox';

if( !directoryExists( testBoxRoot ) ) {

print.boldYellowLine( 'Installing some missing dependencies for you!' ).toConsole();
command( 'install' )
.inWorkingDirectory( excerciseRoot )
.run();
}

// Bootstrap TestBox framework
filesystemUtil.createMapping( '/testbox', testBoxRoot );
}

/**
* Invoke TestBox to run the test suite
*/
private function runTests( boolean solution=false ) {

// Create TestBox and run the tests
testData = new testbox.system.TestBox()
.runRaw( directory = {
// Find all CFCs...
mapping = filesystemUtil.makePathRelative( getCWD() ),
// ... in this directory ...
recurse = false,
// ... whose name ends in "test"
filter = function( path ) {
return path.reFind( ( solution ? 'Solution' : '' ) & 'Test.cfc$' );
}
} )
.getMemento();

// Print out the results with ANSI formatting for the CLI
getInstance( 'CLIRenderer@testbox-commands' )
.render( print, testData, true );

print.toConsole();

// Set proper exit code
if( testData.totalFail || testData.totalError ) {
setExitCode( 1 );
}
}

}

8 changes: 8 additions & 0 deletions exercises/practice/perfect-numbers/box.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"dependencies":{
"testbox":"^2.5.0+107"
},
"installPaths":{
"testbox":"testbox/"
}
}
Loading

0 comments on commit 166432f

Please sign in to comment.