Skip to content

Commit

Permalink
Add exercise perfect-numbers (exercism#266)
Browse files Browse the repository at this point in the history
* Perfect numbers exercise implementation.

* Implementation of the perfect-numbers exercise.

* Updated JSDoc.

* Fixed typo and removed unneeded line.
  • Loading branch information
komyg authored and matthewmorgan committed Mar 24, 2017
1 parent bccc1fa commit 9b8efa0
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 0 deletions.
7 changes: 7 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"rna-transcription",
"bob",
"gigasecond",
"perfect-numbers",
"word-count",
"pangram",
"beer-song",
Expand Down Expand Up @@ -103,6 +104,12 @@
"difficulty": 1,
"topics": [
]
},
{
"slug": "perfect-numbers",
"difficulty": 1,
"topics": [
]
},
{
"slug": "word-count",
Expand Down
68 changes: 68 additions & 0 deletions exercises/perfect-numbers/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

export default class PerfectNumbers {

/**
* Calculate all the divisors for a given number.
* @param {number} number - natural number.
* @returns {array} array that contains the divisors for a given number NOT including the number itself.
*/
getDivisors(number) {

let i;
let divs = new Array();

// Accepts only natural numbers greater than 1.
if (number <= 1) {
return divs;
}

// 1 always divides everyone!
divs.push(1);

// Calculate the divisors up the the half of the number + 1
for (i = 2; i <= number / 2; i++) {

if (number % i === 0) {
divs.push(i);
}
}

return divs;
}

/**
* Classify a given number as perfect, abundant or deficient.
* @param {number} number - number to be classified. Note: if the number is equal or smaller than 0,
* then returns an error: Classification is only possible for natural numbers.
* @returns {string} - string that contains the number classification (perfect, abundant or deficient).
*/
classify(number) {

let i, sum, result;

// Check if the input is valid
if (number <= 0) {
return 'Classification is only possible for natural numbers.';
}

// Factorize the current number.
const divsArray = this.getDivisors(number);

// Sum the factors.
sum = divsArray.reduce((sum, div) => sum += div, 0);

// Check if the number is perfect.
if (sum === number) {
result = 'perfect';
}
else if (sum > number) {
result = 'abundant';
}
else {
result = 'deficient';
}

return result;
}

}
89 changes: 89 additions & 0 deletions exercises/perfect-numbers/gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
function getInputDirectory(argv) {
if (argv.input) {
return argv.input;
}
return '.';
}

function getOutputDirectory(argv) {
if (argv.output) {
return argv.output;
}
return 'babel-output';
}

const gulp = require('gulp'),
eslint = require('gulp-eslint'),
jasmine = require('gulp-jasmine'),
babel = require('gulp-babel'),
polyfill = require('babel/polyfill'),
del = require('del'),
argv = require('yargs').argv,
inputDir = getInputDirectory(argv),
outputDir = getOutputDirectory(argv);

// Gulp tasks definition

gulp.task('default', [ 'test' ]);

gulp.task('test', [ 'babel' ], function () {
return gulp.src([ outputDir + '/*.spec.js' ])
.pipe(jasmine());
});

gulp.task('babel', function () {
return gulp.src([ inputDir + '/*.js', inputDir + '/lib/*.js' ])
.pipe(babel())
.pipe(gulp.dest(outputDir));
});

gulp.task('lint', function () {
return gulp.src([ inputDir + '/*.js' ])
.pipe(eslint({
envs: [
'es6' //turns on all es6 features except modules
],
rules: {
// full documentation here : http://eslint.org/docs/rules

// Possible errors
'comma-dangle': [2, 'never'], // don't let a comma at the end of object properties or array element
'no-cond-assign': [2, 'always'], // no assignments in conditional statements
'no-console': 2, // no console.log() statements in production code
'no-constant-condition': 2, // no constants in conditional statements
'no-control-regex': 2, // no control characters in regex's
'no-debugger': 2, // no debugger in productin code
'no-dupe-args': 2, // no duplicated arguments in functions
'no-dupe-keys': 2, // no duplicated keys when creating object literals
'no-duplicate-case': 2, // no duplicated `case` in `switch` statements
'no-empty-character-class': 2, // disallow the use of empty character classes in regular expressions
'no-empty': 2, // no empty blocks
'no-ex-assign': 2, // do not assign any value to an Exception raised
'no-extra-boolean-cast': 2, // do not use !!falseExpression to cast to boolean
'no-extra-parens': 2, // do not use extra parenthesis
'no-extra-semi': 2, // do not use extra semicolons
'no-func-assign': 2, // do not overwrite variables declared as functions
'no-inner-declarations': [2, 'both'], // only declare var's and funct's on function scope
'no-invalid-regexp': 2, // validates string arguments passed to RegExp constructor
'no-irregular-whitespace': 2, // detects special characters used as spaces
'no-negated-in-lhs': 2, // do not use negation in the left operand in an `in` expression
'no-obj-calls': 2, // prevent calling global objects as if they were functions
'no-regex-spaces': 2, // do not use multiple spaces in regex's
'no-sparse-arrays': 2, // do not use sparse arrays (empty elements)
'no-unexpected-multiline': 2, // Avoid code that looks like two expressions but is actually one
'no-unreachable': 2, // detects unreachable statements (after return, throw,...)
'use-isnan': 2, // do not compare with `NaN` value, use isNan() instead
'valid-jsdoc': 2, // ensure JSDoc comments are valid
'valid-typeof': 2 // ensure that the results of typeof are compared against a valid string
},
ecmaFeatures: {
'modules': true //this gives us modules :)
}
}))
.pipe(eslint.format())
.pipe(eslint.failAfterError());
});

gulp.task('clean', function (cb) {
del([ outputDir ], cb);
});
28 changes: 28 additions & 0 deletions exercises/perfect-numbers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "xecmascript",
"version": "0.0.0",
"description": "Exercism exercises in ECMAScript 6.",
"author": "Katrina Owen",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/exercism/xecmascript"
},
"devDependencies": {
"babel": "~5.8.29",
"del": "~2.0.2",
"gulp": "~3.9.0",
"gulp-babel": "~5.3.0",
"gulp-eslint": "^1.1.0",
"gulp-jasmine": "~2.4.2",
"yargs": "~3.27.0"
},
"scripts": {
"test": "gulp test",
"lint-test": "gulp lint test"
},
"licenses": [
"MIT"
],
"dependencies": {}
}
75 changes: 75 additions & 0 deletions exercises/perfect-numbers/perfect-numbers.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import PerfectNumbers from './perfect-numbers';

describe('Exercise - Perfect Numbers', () => {

const perfectNumbers = new PerfectNumbers();

describe('Perfect Numbers', () => {

it('Smallest perfect number is classified correctly', () => {
expect(perfectNumbers.classify(6)).toEqual('perfect');
});

it('Medium perfect number is classified correctly', () => {
expect(perfectNumbers.classify(28)).toEqual('perfect');
});

it('Large perfect number is classified correctly', () => {
expect(perfectNumbers.classify(33550336)).toEqual('perfect');
});

});

describe('Abundant Numbers', () => {

it('Smallest abundant number is classified correctly', () => {
expect(perfectNumbers.classify(12)).toEqual('abundant');
});

it('Medium abundant number is classified correctly', () => {
expect(perfectNumbers.classify(30)).toEqual('abundant');
});

it('Large abundant number is classified correctly', () => {
expect(perfectNumbers.classify(33550335)).toEqual('abundant');
});

});

describe('Deficient Numbers', () => {

it('Smallest prime deficient number is classified correctly', () => {
expect(perfectNumbers.classify(2)).toEqual('deficient');
});

it('Smallest non-prime deficient number is classified correctly', () => {
expect(perfectNumbers.classify(4)).toEqual('deficient');
});

it('Medium deficient number is classified correctly', () => {
expect(perfectNumbers.classify(32)).toEqual('deficient');
});

it('Large deficient number is classified correctly', () => {
expect(perfectNumbers.classify(33550337)).toEqual('deficient');
});

it('Edge case (no factors other than itself) is classified correctly', () => {
expect(perfectNumbers.classify(1)).toEqual('deficient');
});

});

describe('Invalid Inputs', () => {

it('Zero is rejected (not a natural number)', () => {
expect(perfectNumbers.classify(0)).toEqual('Classification is only possible for natural numbers.');
});

it('Negative integer is rejected (not a natural number)', () => {
expect(perfectNumbers.classify(-1)).toEqual('Classification is only possible for natural numbers.');
});

});

});

0 comments on commit 9b8efa0

Please sign in to comment.