Home Manual Reference Source

src/IntegerRing.js

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;
	}
}