aureooms/js-rational-field

View on GitHub
src/_make_rational_class.js

Summary

Maintainability
C
1 day
Test Coverage
F
59%
import _make_methods from './_make_methods.js';

import {ZeroDivisionError} from '@aureooms/js-integer';

export default function _make_rational_class(domain, default_display_base) {
    const {
        add,
        sub,
        mul,
        div,
        pow,
        cmp,
        simplify,
        stringify_fixed_point,
        stringify_fraction,
        parse_fraction,
        parse_fixed_point,
    } = _make_methods(domain);

    class Rational {
        constructor(n, d) {
            this.n = n;
            this.d = d;
        }

        add(other) {
            const [a, b] = add(this.n, this.d, other.n, other.d);
            const [c, d] = simplify(a, b);
            return new Rational(c, d);
        }

        sub(other) {
            const [a, b] = sub(this.n, this.d, other.n, other.d);
            const [c, d] = simplify(a, b);
            return new Rational(c, d);
        }

        mul(other) {
            const [a, b] = mul(this.n, this.d, other.n, other.d);
            const [c, d] = simplify(a, b);
            return new Rational(c, d);
        }

        div(other) {
            if (other.isZero()) throw new ZeroDivisionError('Rational.div');
            const [a, b] = div(this.n, this.d, other.n, other.d);
            const [c, d] = simplify(a, b);
            return new Rational(c, d);
        }

        neg() {
            return new Rational(domain.neg(this.n), this.d);
        }

        isZero() {
            return domain.jz(this.n);
        }

        sign() {
            return domain.sgn(this.n);
        }

        inv() {
            if (domain.jz(this.n)) throw new ZeroDivisionError('Rational.inv');
            if (domain.sign(this.n) > 0) return new Rational(this.d, this.n);
            if (domain.sign(this.n) < 0)
                return new Rational(domain.neg(this.d), domain.neg(this.n));
        }

        pow(n) {
            if (!Number.isInteger(n))
                throw new Error('Rational.pow only accepts integer argument.');
            const [a, b] = pow(this.n, this.d, n);
            return new Rational(a, b);
        }

        cmp(other) {
            return cmp(this.n, this.d, other.n, other.d);
        }

        eq(other) {
            return this.cmp(other) === 0;
        }

        ne(other) {
            return this.cmp(other) !== 0;
        }

        gt(other) {
            return this.cmp(other) > 0;
        }

        lt(other) {
            return this.cmp(other) < 0;
        }

        ge(other) {
            return this.cmp(other) >= 0;
        }

        le(other) {
            return this.cmp(other) <= 0;
        }

        toString(base = default_display_base) {
            return stringify_fraction(this.n, this.d, base);
        }

        toNumber() {
            throw new Error('Rational.toNumber not implemented.');
        }

        toFixed(decimals = undefined, base = default_display_base) {
            const s = stringify_fixed_point(this.n, this.d, base);
            if (decimals === undefined) return s;

            const [left, right] = (s + '.').split('.');
            const [transient, repetend] = (right + '|0').split('|');

            const _right = (
                transient +
                new Array(Math.ceil(decimals / repetend.length) + 1).join(repetend)
            ).slice(0, decimals);

            return left + '.' + _right.slice(0, decimals);
        }
    }

    Rational._parse_fraction = parse_fraction;
    Rational._parse_fixed_point = parse_fixed_point;
    Rational._simplify = simplify;

    return Rational;
}