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

fwas's solution

to Rational Numbers in the JavaScript Track

Published at Apr 28 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 qth 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

Wikipedia https://en.wikipedia.org/wiki/Rational_number

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);
  });
});
//
// This is only a SKELETON file for the 'Rational Numbers' exercise. It's been provided as a
// convenience to get you started writing code faster.
//

export class Rational {
  constructor(one,two) {
    this.top = one;
    this.bot = two;

    if (this.bot == 0) {
      throw new Error('Division by zero!')
    }
  }

  add(number) {
    return this.reduce(new Rational((this.top*number.bot)+(number.top*this.bot), (this.bot*number.bot)));
  }

  sub(number) {
    return this.reduce(new Rational((this.top*number.bot)-(number.top*this.bot), (this.bot*number.bot)));
  }

  mul(number) {
    return this.reduce(new Rational((this.top*number.top), (this.bot*number.bot)));
  }

  div(number) {
    return this.reduce(new Rational((this.top*number.bot),(number.top*this.bot)));
  }

  abs(number) {
    if (!number) {
      number = this;
    }
    if (number.top < 0) {
      number.top*=-1
    }
    if (number.bot<0) {
      number.bot*=-1
    }
    return this.reduce(new Rational(number.top, number.bot))
  }

  exprational(number) {
    var tempTop = 1;
    var tempBot = 1;
    for (var i = 0; i < number; i++) {
      tempTop = tempTop*this.top;
      tempBot =tempBot*this.bot;
    }
    return this.reduce(new Rational(tempTop, tempBot))
  }

  expreal(number) {
    return Math.pow(Math.pow(number,this.top), (1/this.bot));
  }

  reduce(number) {
    if (!number) {
      number = this;
    }
    var a = number.top;
    var b = number.bot;
    var c;
    while (b!=0) {
      c=b;
      b=a%b;
      a=c;
    }
    if (a<0) {
      a=a*(-1)
    }
    var retTop = number.top/a
    var retBot = number.bot/a
    if ((retBot<0 && retTop>0) || (retBot < 0 && retTop < 0)) {
      retTop*=-1
      retBot*=-1
    }
    if (a != retBot || retTop == 0 || retBot == retTop) {
      return new Rational(retTop, retBot);
    }else {
      return new Rational(retTop, retBot);
    }
  }
}

Community comments

Find this solution interesting? Ask the author a question to learn more.

fwas's Reflection

Test "Raise a real number to a positive rational number" doesn't pass natively, because the answer I get is something like 15.99999999999 instead of 16.0. I could round it up, but I decided to change the test condition from .toEqual() to .toBeCloseTo().