aureooms/js-integer

View on GitHub
src/IntegerRing.js

Summary

Maintainability
B
4 hrs
Test Coverage
A
100%
import {parse, convert} from '@aureooms/js-integer-big-endian';
import {TypeError, ValueError} from '@aureooms/js-error';

import {Integer} from './Integer.js';
import {DEFAULT_DISPLAY_BASE} from './DEFAULT_DISPLAY_BASE.js';
import {_from_number} from './_from_number.js';

export class IntegerRing {
    constructor(name, base) {
        this.name = name;
        this._base = base;
    }

    characteristic() {
        return this.$0();
    }

    from(object, base = undefined, is_negative = 0) {
        if (object === null || object === undefined) return this.$0();

        switch (object.constructor.prototype) {
            case Number.prototype:
                if (base !== undefined)
                    throw new ValueError(
                        'IntegerRing#from: using the base parameter does not make sense when passing a Number.',
                    );
                return this.from_number(object, is_negative);

            case String.prototype:
                if (base === undefined) base = DEFAULT_DISPLAY_BASE;
                return this.from_string(object, base, is_negative);

            case Array.prototype:
                if (base === undefined) base = this._base;
                return this.from_digits(object, base, is_negative);

            case Boolean.prototype:
                if (base !== undefined)
                    throw new ValueError(
                        'IntegerRing#from: using the base parameter does not make sense when passing a Boolean.',
                    );
                return this.from_number(Number(object), is_negative);

            case Integer.prototype:
                if (base !== undefined)
                    throw new ValueError(
                        'IntegerRing#from: using the base parameter does not make sense when passing an Integer.',
                    );
                return new Integer(
                    object._base,
                    object._is_negative ^ is_negative,
                    object._limbs,
                );

            default:
                throw new TypeError(
                    `IntegerRing#from cannot handle ${object.constructor.prototype}`,
                );
        }
    }

    from_number(number, is_negative = 0) {
        const dirty = _from_number(number);

        const limbs = dirty._limbs_in_base(this._base);

        return new Integer(this._base, is_negative ^ dirty._is_negative, limbs);
    }

    from_string(string, base = 10, is_negative = 0) {
        if (string.length === 0)
            throw new ValueError(
                'IntegerRing#from_string cannot parse empty string.',
            );

        if (string[0] === '-')
            return this.from_string(string.slice(1), base, ~is_negative);

        if (string[0] === '+')
            return this.from_string(string.slice(1), base, is_negative);

        const limbs = parse(base, this._base, string);

        if (limbs.length === 1 && limbs[0] === 0) is_negative = 0;

        return new Integer(this._base, is_negative, limbs);
    }

    from_digits(digits, base, is_negative) {
        const limbs = convert(
            base,
            this._base,
            digits.slice().reverse(),
            0,
            digits.length,
        );

        return new Integer(this._base, is_negative, limbs);
    }

    toString() {
        return this.name;
    }

    $0() {
        // TODO Could we use an empty array instead ?
        return new Integer(this._base, 0, [0]);
    }

    $1() {
        return new Integer(this._base, 0, [1]);
    }

    $_1() {
        return new Integer(this._base, -1, [1]);
    }

    has(x) {
        if (x instanceof Integer) return true;
        return Number.isInteger(x);
    }

    min(a, b) {
        return a.le(b) ? a : b;
    }

    max(a, b) {
        return a.ge(b) ? a : b;
    }
}