🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉

itsrani's solution

to Rational Numbers in the JavaScript Track

Published at Mar 05 2021 · 0 comments
Instructions
Test suite
Solution

A rational number is defined as the quotient of two integers `a` and `b`, called the numerator and denominator, respectively, where `b != 0`.

The absolute value `|r|` of the rational number `r = a/b` is equal to `|a|/|b|`.

The sum of two rational numbers `r₁ = a₁/b₁` and `r₂ = a₂/b₂` is `r₁ + r₂ = a₁/b₁ + a₂/b₂ = (a₁ * b₂ + a₂ * b₁) / (b₁ * b₂)`.

The difference of two rational numbers `r₁ = a₁/b₁` and `r₂ = a₂/b₂` is `r₁ - r₂ = a₁/b₁ - a₂/b₂ = (a₁ * b₂ - a₂ * b₁) / (b₁ * b₂)`.

The product (multiplication) of two rational numbers `r₁ = a₁/b₁` and `r₂ = a₂/b₂` is `r₁ * r₂ = (a₁ * a₂) / (b₁ * b₂)`.

Dividing a rational number `r₁ = a₁/b₁` by another `r₂ = a₂/b₂` is `r₁ / r₂ = (a₁ * b₂) / (a₂ * b₁)` if `a₂` is not zero.

Exponentiation of a rational number `r = a/b` to a non-negative integer power `n` is `r^n = (a^n)/(b^n)`.

Exponentiation of a rational number `r = a/b` to a negative integer power `n` is `r^n = (b^m)/(a^m)`, where `m = |n|`.

Exponentiation of a rational number `r = a/b` to a real (floating-point) number `x` is the quotient `(a^x)/(b^x)`, which is a real number.

Exponentiation of a real number `x` to a rational number `r = a/b` is `x^(a/b) = root(x^a, b)`, where `root(p, q)` is the `q`th root of `p`.

Implement the following operations:

• addition, subtraction, multiplication and division of two rational numbers,
• absolute value, exponentiation of a given rational number to an integer power, exponentiation of a given rational number to a real (floating-point) power, exponentiation of a real number to a rational number.

Your implementation of rational numbers should always be reduced to lowest terms. For example, `4/4` should reduce to `1/1`, `30/60` should reduce to `1/2`, `12/8` should reduce to `3/2`, etc. To reduce a rational number `r = a/b`, divide `a` and `b` by the greatest common divisor (gcd) of `a` and `b`. So, for example, `gcd(12, 8) = 4`, so `r = 12/8` can be reduced to `(12/4)/(8/4) = 3/2`.

Assume that the programming language you are using does not have an implementation of rational numbers.

Setup

Go through the setup instructions for Javascript to install the necessary dependencies:

https://exercism.io/tracks/javascript/installation

Requirements

Please `cd` into exercise directory before running all below commands.

Install assignment dependencies:

``````\$ npm install
``````

Making the test suite pass

Execute the tests with:

``````\$ 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`.

Submitting Solutions

Once you have a solution ready, you can submit it using:

``````exercism submit rational-numbers.js
``````

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

Exercise Source Credits

rational-numbers.spec.js

``````import { Rational } from './rational-numbers';

describe('Addition', () => {
test('Add two positive rational numbers', () => {
const expected = new Rational(7, 6);
expect(new Rational(1, 2).add(new Rational(2, 3))).toEqual(expected);
});

xtest('Add a positive rational number and a negative rational number', () => {
const expected = new Rational(-1, 6);
expect(new Rational(1, 2).add(new Rational(-2, 3))).toEqual(expected);
});

xtest('Add two negative rational numbers', () => {
const expected = new Rational(-7, 6);
expect(new Rational(-1, 2).add(new Rational(-2, 3))).toEqual(expected);
});

xtest('Add a rational number to its additive inverse', () => {
const expected = new Rational(0, 1);
expect(new Rational(1, 2).add(new Rational(-1, 2))).toEqual(expected);
});
});

describe('Subtraction', () => {
xtest('Subtract two positive rational numbers', () => {
const expected = new Rational(-1, 6);
expect(new Rational(1, 2).sub(new Rational(2, 3))).toEqual(expected);
});

xtest('Subtract a positive rational number and a negative rational number', () => {
const expected = new Rational(7, 6);
expect(new Rational(1, 2).sub(new Rational(-2, 3))).toEqual(expected);
});

xtest('Subtract two negative rational numbers', () => {
const expected = new Rational(1, 6);
expect(new Rational(-1, 2).sub(new Rational(-2, 3))).toEqual(expected);
});

xtest('Subtract a rational number from itself', () => {
const expected = new Rational(0, 1);
expect(new Rational(1, 2).sub(new Rational(1, 2))).toEqual(expected);
});
});

describe('Multiplication', () => {
xtest('Multiply two positive rational numbers', () => {
const expected = new Rational(1, 3);
expect(new Rational(1, 2).mul(new Rational(2, 3))).toEqual(expected);
});

xtest('Multiply a negative rational number by a positive rational number', () => {
const expected = new Rational(-1, 3);
expect(new Rational(-1, 2).mul(new Rational(2, 3))).toEqual(expected);
});

xtest('Multiply two negative rational numbers', () => {
const expected = new Rational(1, 3);
expect(new Rational(-1, 2).mul(new Rational(-2, 3))).toEqual(expected);
});

xtest('Multiply a rational number by its reciprocal', () => {
const expected = new Rational(1, 1);
expect(new Rational(1, 2).mul(new Rational(2, 1))).toEqual(expected);
});

xtest('Multiply a rational number by 1', () => {
const expected = new Rational(1, 2);
expect(new Rational(1, 2).mul(new Rational(1, 1))).toEqual(expected);
});

xtest('Multiply a rational number by 0', () => {
const expected = new Rational(0, 1);
expect(new Rational(1, 2).mul(new Rational(0, 1))).toEqual(expected);
});
});

describe('Division', () => {
xtest('Divide two positive rational numbers', () => {
const expected = new Rational(3, 4);
expect(new Rational(1, 2).div(new Rational(2, 3))).toEqual(expected);
});

xtest('Divide a positive rational number by a negative rational number', () => {
const expected = new Rational(-3, 4);
expect(new Rational(1, 2).div(new Rational(-2, 3))).toEqual(expected);
});

xtest('Divide two negative rational numbers', () => {
const expected = new Rational(3, 4);
expect(new Rational(-1, 2).div(new Rational(-2, 3))).toEqual(expected);
});

xtest('Divide a rational number by 1', () => {
const expected = new Rational(1, 2);
expect(new Rational(1, 2).div(new Rational(1, 1))).toEqual(expected);
});
});

describe('Absolute value', () => {
xtest('Absolute value of a positive rational number', () => {
const expected = new Rational(1, 2);
expect(new Rational(1, 2).abs()).toEqual(expected);
});

xtest('Absolute value of a negative rational number', () => {
const expected = new Rational(1, 2);
expect(new Rational(-1, 2).abs()).toEqual(expected);
});

xtest('Absolute value of zero', () => {
const expected = new Rational(0, 1);
expect(new Rational(0, 1).abs()).toEqual(expected);
});
});

describe('Exponentiation of a rational number', () => {
xtest('Raise a positive rational number to a positive integer power', () => {
const expected = new Rational(1, 8);
expect(new Rational(1, 2).exprational(3)).toEqual(expected);
});

xtest('Raise a negative rational number to a positive integer power', () => {
const expected = new Rational(-1, 8);
expect(new Rational(-1, 2).exprational(3)).toEqual(expected);
});

xtest('Raise zero to an integer power', () => {
const expected = new Rational(0, 1);
expect(new Rational(0, 1).exprational(5)).toEqual(expected);
});

xtest('Raise one to an integer power', () => {
const expected = new Rational(1, 1);
expect(new Rational(1, 1).exprational(4)).toEqual(expected);
});

xtest('Raise a positive rational number to the power of zero', () => {
const expected = new Rational(1, 1);
expect(new Rational(1, 2).exprational(0)).toEqual(expected);
});

xtest('Raise a negative rational number to the power of zero', () => {
const expected = new Rational(1, 1);
expect(new Rational(-1, 2).exprational(0)).toEqual(expected);
});
});

describe('Exponentiation of a real number to a rational number', () => {
xtest('Raise a real number to a positive rational number', () => {
const expected = 16.0;
expect(new Rational(4, 3).expreal(8)).toEqual(expected);
});

xtest('Raise a real number to a negative rational number', () => {
expect(new Rational(-1, 2).expreal(9)).toBeCloseTo(0.33, 2);
});

xtest('Raise a real number to a zero rational number', () => {
const expected = 1.0;
expect(new Rational(0, 1).expreal(2)).toEqual(expected);
});
});

describe('Reduction to lowest terms', () => {
xtest('Reduce a positive rational number to lowest terms', () => {
const expected = new Rational(1, 2);
expect(new Rational(2, 4).reduce()).toEqual(expected);
});

xtest('Reduce a negative rational number to lowest terms', () => {
const expected = new Rational(-2, 3);
expect(new Rational(-4, 6).reduce()).toEqual(expected);
});

xtest('Reduce a rational number with a negative denominator to lowest terms', () => {
const expected = new Rational(-1, 3);
expect(new Rational(3, -9).reduce()).toEqual(expected);
});

xtest('Reduce zero to lowest terms', () => {
const expected = new Rational(0, 1);
expect(new Rational(0, 6).reduce()).toEqual(expected);
});

xtest('Reduce an integer to lowest terms', () => {
const expected = new Rational(-2, 1);
expect(new Rational(-14, 7).reduce()).toEqual(expected);
});

xtest('Reduce one to lowest terms', () => {
const expected = new Rational(1, 1);
expect(new Rational(13, 13).reduce()).toEqual(expected);
});
});``````
``````export class Rational {
constructor(numerator, denominator) {
this.numerator = numerator;
this.denominator = denominator;
}

return new Rational(
this.numerator * r.denominator + r.numerator * this.denominator,
this.denominator * r.denominator
).reduce();
}

sub(r) {
return new Rational(
this.numerator * r.denominator - r.numerator * this.denominator,
this.denominator * r.denominator
).reduce();
}

mul(r) {
return new Rational(
this.numerator * r.numerator,
this.denominator * r.denominator
).reduce();
}

div(r) {
return new Rational(
this.numerator * r.denominator,
r.numerator * this.denominator
).reduce();
}

abs() {
return new Rational(Math.abs(this.numerator), Math.abs(this.denominator));
}

reduce() {
var divisor = this.gcd(
Math.abs(this.numerator),
Math.abs(this.denominator)
);

if (this.denominator >= 0) {
return new Rational(this.numerator / divisor, this.denominator / divisor);
}

return new Rational(
(this.numerator * -1) / divisor,
(this.denominator * -1) / divisor
);
}

exprational(power) {
return power >= 0
? new Rational(
Math.pow(this.numerator, power),
Math.pow(this.denominator, power)
).reduce()
: new Rational(
Math.pow(this.denominator, Math.abs(power)),
Math.pow(this.numerator, Math.abs(power))
).reduce();
}

expreal(baseNumber) {
return this.nthRoot(
Math.pow(baseNumber, this.numerator),
this.denominator,
1e-9
);
}

nthRoot(A, n, p) {
var x = [];
x[0] = A;
x[1] = A / n;
while (Math.abs(x[0] - x[1]) > p) {
x[1] = x[0];
x[0] = (1 / n) * ((n - 1) * x[1] + A / Math.pow(x[1], n - 1));
}

return x[0];
}

gcd(x, y) {
return y == 0 ? x : this.gcd(y, x % y);
}
}``````