diff --git a/config.json b/config.json index 36f3c8d8ef..9470286148 100644 --- a/config.json +++ b/config.json @@ -1133,6 +1133,16 @@ "Regular expressions" ] }, + { + "uuid": "a0eecb1c-0dd1-4180-1d19-c6ab92bc23ca163ee56", + "slug": "complex-numbers", + "core": false, + "unlocked_by": "space-age", + "difficulty": 4, + "topics": [ + "Mathematics" + ] + }, { "uuid": "213ea396-0c25-e480-4e76-5876975bdd54ac4890c", "slug": "isbn-verifier", diff --git a/exercises/complex-numbers/README.md b/exercises/complex-numbers/README.md new file mode 100644 index 0000000000..dc6587f550 --- /dev/null +++ b/exercises/complex-numbers/README.md @@ -0,0 +1,68 @@ +# Complex numbers + +A complex number is a number in the form `a + b * i` where `a` and `b` are real and `i` satisfies `i^2 = -1`. + +`a` is called the real part and `b` is called the imaginary part of `z`. +The conjugate of the number `a + b * i` is the number `a - b * i`. +The absolute value of a complex number `z = a + b * i` is a real number `|z| = sqrt(a^2 + b^2)`. The square of the absolute value `|z|^2` is the result of multiplication of `z` by its complex conjugate. + +The sum/difference of two complex numbers involves adding/subtracting their real and imaginary parts separately: +`(a + i * b) + (c + i * d) = (a + c) + (b + d) * i`, +`(a + i * b) - (c + i * d) = (a - c) + (b - d) * i`. + +Multiplication result is by definition +`(a + i * b) * (c + i * d) = (a * c - b * d) + (b * c + a * d) * i`. + +The reciprocal of a non-zero complex number is +`1 / (a + i * b) = a/(a^2 + b^2) - b/(a^2 + b^2) * i`. + +Dividing a complex number `a + i * b` by another `c + i * d` gives: +`(a + i * b) / (c + i * d) = (a * c + b * d)/(c^2 + d^2) + (b * c - a * d)/(c^2 + d^2) * i`. + +Exponent of a complex number can be expressed as +`exp(a + i * b) = exp(a) * exp(i * b)`, +and the last term is given by Euler's formula `exp(i * b) = cos(b) + i * sin(b)`. + + +Implement the following operations: + - addition, subtraction, multiplication and division of two complex numbers, + - conjugate, absolute value, exponent of a given complex number. + + +Assume the programming language you are using does not have an implementation of complex numbers. + +## Setup + +Go through the setup instructions for ECMAScript to +install the necessary dependencies: + +http://exercism.io/languages/ecmascript/installation + +## Requirements + +Install assignment dependencies: + +```bash +$ npm install +``` + +## Making the test suite pass + +Execute the tests with: + +```bash +$ npm test +``` + +In the test suites all tests but the first have been skipped. + +Once you get a test passing, you can enable the next one by +changing `xtest` to `test`. + + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Complex_number](https://en.wikipedia.org/wiki/Complex_number) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/complex-numbers/complex-numbers.spec.js b/exercises/complex-numbers/complex-numbers.spec.js new file mode 100644 index 0000000000..f63277e6f2 --- /dev/null +++ b/exercises/complex-numbers/complex-numbers.spec.js @@ -0,0 +1,216 @@ +import ComplexNumber from './complex-numbers.js'; + +describe('Complex numbers', () => { + test('Real part of a purely real number', () => { + const expected = 1; + const actual = new ComplexNumber(1, 0).real; + + expect(actual).toEqual(expected); + }); + + xtest('Real part of a purely imaginary number', () => { + const expected = 0; + const actual = new ComplexNumber(0, 1).real; + + expect(actual).toEqual(expected); + }); + + xtest('Real part of a number with real and imaginary part', () => { + const expected = 1; + const actual = new ComplexNumber(1, 2).real; + + expect(actual).toEqual(expected); + }); + + xtest('Imaginary part of a purely real number', () => { + const expected = 0; + const actual = new ComplexNumber(1, 0).imag; + + expect(actual).toEqual(expected); + }); + + xtest('Imaginary part of a purely imaginary number', () => { + const expected = 1; + const actual = new ComplexNumber(0, 1).imag; + + expect(actual).toEqual(expected); + }); + + xtest('Imaginary part of a number with real and imaginary part', () => { + const expected = 2; + const actual = new ComplexNumber(1, 2).imag; + + expect(actual).toEqual(expected); + }); + + xtest('Add purely real numbers', () => { + const expected = new ComplexNumber(3, 0); + const actual = new ComplexNumber(1, 0).add(new ComplexNumber(2, 0)); + + expect(actual).toEqual(expected); + }); + + xtest('Add purely imaginary numbers', () => { + const expected = new ComplexNumber(0, 3); + const actual = new ComplexNumber(0, 1).add(new ComplexNumber(0, 2)); + + expect(actual).toEqual(expected); + }); + + xtest('Add numbers with real and imaginary part', () => { + const expected = new ComplexNumber(4, 6); + const actual = new ComplexNumber(1, 2).add(new ComplexNumber(3, 4)); + + expect(actual).toEqual(expected); + }); + + xtest('Subtract purely real numbers', () => { + const expected = new ComplexNumber(-1, 0); + const actual = new ComplexNumber(1, 0).sub(new ComplexNumber(2, 0)); + + expect(actual).toEqual(expected); + }); + + xtest('Subtract purely imaginary numbers', () => { + const expected = new ComplexNumber(0, -1); + const actual = new ComplexNumber(0, 1).sub(new ComplexNumber(0, 2)); + + expect(actual).toEqual(expected); + }); + + xtest('Subtract numbers with real and imaginary part', () => { + const expected = new ComplexNumber(-2, -2); + const actual = new ComplexNumber(1, 2).sub(new ComplexNumber(3, 4)); + + expect(actual).toEqual(expected); + }); + + xtest('Multiply purely real numbers', () => { + const expected = new ComplexNumber(2, 0); + const actual = new ComplexNumber(1, 0).mul(new ComplexNumber(2, 0)); + + expect(actual).toEqual(expected); + }); + + xtest('Multiply imaginary unit', () => { + const expected = new ComplexNumber(-1, 0); + const actual = new ComplexNumber(0, 1).mul(new ComplexNumber(0, 1)); + + expect(actual).toEqual(expected); + }); + + xtest('Multiply purely imaginary numbers', () => { + const expected = new ComplexNumber(-2, 0); + const actual = new ComplexNumber(0, 1).mul(new ComplexNumber(0, 2)); + + expect(actual).toEqual(expected); + }); + + xtest('Multiply numbers with real and imaginary part', () => { + const expected = new ComplexNumber(-5, 10); + const actual = new ComplexNumber(1, 2).mul(new ComplexNumber(3, 4)); + + expect(actual).toEqual(expected); + }); + + xtest('Divide purely real numbers', () => { + const expected = new ComplexNumber(0.5, 0); + const actual = new ComplexNumber(1, 0).div(new ComplexNumber(2, 0)); + + expect(actual).toEqual(expected); + }); + + xtest('Divide purely imaginary numbers', () => { + const expected = new ComplexNumber(0.5, 0); + const actual = new ComplexNumber(0, 1).div(new ComplexNumber(0, 2)); + + expect(actual).toEqual(expected); + }); + + xtest('Divide numbers with real and imaginary part', () => { + const expected = new ComplexNumber(0.44, 0.08); + const actual = new ComplexNumber(1, 2).div(new ComplexNumber(3, 4)); + + expect(actual).toEqual(expected); + }); + + xtest('Absolute value of a positive purely real number', () => { + const expected = 5; + const actual = new ComplexNumber(5, 0).abs; + + expect(actual).toEqual(expected); + }); + + xtest('Absolute value of a negative purely real number', () => { + const expected = 5; + const actual = new ComplexNumber(-5, 0).abs; + + expect(actual).toEqual(expected); + }); + + xtest('Absolute value of a purely imaginary number with positive imaginary part', () => { + const expected = 5; + const actual = new ComplexNumber(0, 5).abs; + + expect(actual).toEqual(expected); + }); + + xtest('Absolute value of a purely imaginary number with negative imaginary part', () => { + const expected = 5; + const actual = new ComplexNumber(0, -5).abs; + + expect(actual).toEqual(expected); + }); + + xtest('Absolute value of a number with real and imaginary part', () => { + const expected = 5; + const actual = new ComplexNumber(3, 4).abs; + + expect(actual).toEqual(expected); + }); + + xtest('Conjugate a purely real number', () => { + const expected = new ComplexNumber(5, 0); + const actual = new ComplexNumber(5, 0).conj; + + expect(actual).toEqual(expected); + }); + + xtest('Conjugate a purely imaginary number', () => { + const expected = new ComplexNumber(0, -5); + const actual = new ComplexNumber(0, 5).conj; + + expect(actual).toEqual(expected); + }); + + xtest('Conjugate a number with real and imaginary part', () => { + const expected = new ComplexNumber(1, -1); + const actual = new ComplexNumber(1, 1).conj; + + expect(actual).toEqual(expected); + }); + + xtest('Euler\'s identity/formula', () => { + const expected = new ComplexNumber(-1, 0); + const actual = new ComplexNumber(0, Math.PI).exp; + + expect(actual.real).toBeCloseTo(expected.real); + expect(actual.imag).toBeCloseTo(expected.imag); + }); + + xtest('Exponential of 0', () => { + const expected = new ComplexNumber(1, 0); + const actual = new ComplexNumber(0, 0).exp; + + expect(actual.real).toBeCloseTo(expected.real); + expect(actual.imag).toBeCloseTo(expected.imag); + }); + + xtest('Exponential of a purely real number', () => { + const expected = new ComplexNumber(Math.E, 0); + const actual = new ComplexNumber(1, 0).exp; + + expect(actual.real).toBeCloseTo(expected.real); + expect(actual.imag).toBeCloseTo(expected.imag); + }); +}); diff --git a/exercises/complex-numbers/example.js b/exercises/complex-numbers/example.js new file mode 100644 index 0000000000..34108094b9 --- /dev/null +++ b/exercises/complex-numbers/example.js @@ -0,0 +1,43 @@ +export default class ComplexNumber { + + constructor(real, imag) { + this.real = real; + this.imag = imag; + } + + add(other) { + return new ComplexNumber(this.real + other.real, this.imag + other.imag); + } + + sub(other) { + return new ComplexNumber(this.real - other.real, this.imag - other.imag); + } + + mul(other) { + return new ComplexNumber( + (this.real * other.real) - (this.imag * other.imag), + (this.imag * other.real) + (this.real * other.imag)); + } + + div(other) { + return new ComplexNumber( + ((this.real * other.real) + (this.imag * other.imag)) + / ((other.real * other.real) + (other.imag * other.imag)), + ((this.imag * other.real) - (this.real * other.imag)) + / ((other.real * other.real) + (other.imag * other.imag))); + } + + get abs() { + return Math.sqrt((this.real * this.real) + (this.imag * this.imag)); + } + + get conj() { + return new ComplexNumber(this.real, this.imag !== 0 ? -this.imag : 0); + } + + get exp() { + return new ComplexNumber( + Math.exp(this.real) * Math.cos(this.imag), + Math.exp(this.real) * Math.sin(this.imag)); + } +} diff --git a/exercises/complex-numbers/package.json b/exercises/complex-numbers/package.json new file mode 100644 index 0000000000..19688acdb3 --- /dev/null +++ b/exercises/complex-numbers/package.json @@ -0,0 +1,71 @@ +{ + "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-jest": "^21.2.0", + "babel-plugin-transform-builtin-extend": "^1.1.2", + "babel-preset-env": "^1.4.0", + "eslint": "^3.19.0", + "eslint-config-airbnb": "^15.0.1", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^5.0.1", + "eslint-plugin-react": "^7.0.1", + "jest": "^21.2.1" + }, + "jest": { + "modulePathIgnorePatterns": [ + "package.json" + ] + }, + "babel": { + "presets": [ + "env" + ], + "plugins": [ + [ + "babel-plugin-transform-builtin-extend", + { + "globals": [ + "Error" + ] + } + ], + [ + "transform-regenerator" + ] + ] + }, + "scripts": { + "test": "jest --no-cache ./*", + "watch": "jest --no-cache --watch ./*", + "lint": "eslint .", + "lint-test": "eslint . && jest --no-cache ./* " + }, + "eslintConfig": { + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "env": { + "es6": true, + "node": true, + "jest": true + }, + "extends": "airbnb", + "rules": { + "import/no-unresolved": "off", + "import/extensions": "off" + } + }, + "licenses": [ + "MIT" + ], + "dependencies": {} +}