UNPKG

@nuintun/qrcode

Version:

A pure JavaScript QRCode encode and decode library.

167 lines (163 loc) 5.36 kB
/** * @module QRCode * @package @nuintun/qrcode * @license MIT * @version 5.0.2 * @author nuintun <nuintun@qq.com> * @description A pure JavaScript QRCode encode and decode library. * @see https://github.com/nuintun/qrcode#readme */ 'use strict'; /** * @module Polynomial */ class Polynomial { #field; #coefficients; constructor(field, coefficients) { const { length } = coefficients; if (length <= 0) { throw new Error('polynomial coefficients cannot empty'); } this.#field = field; if (length > 1 && coefficients[0] === 0) { // Leading term must be non-zero for anything except the constant polynomial "0". let firstNonZero = 1; while (firstNonZero < length && coefficients[firstNonZero] === 0) { firstNonZero++; } if (firstNonZero === length) { this.#coefficients = new Int32Array([0]); } else { const array = new Int32Array(length - firstNonZero); array.set(coefficients.subarray(firstNonZero)); this.#coefficients = array; } } else { this.#coefficients = coefficients; } } get coefficients() { return this.#coefficients; } isZero() { return this.#coefficients[0] === 0; } getDegree() { return this.#coefficients.length - 1; } getCoefficient(degree) { const coefficients = this.#coefficients; return coefficients[coefficients.length - 1 - degree]; } evaluate(a) { if (a === 0) { // Just return the x^0 coefficient. return this.getCoefficient(0); } let result; const coefficients = this.#coefficients; if (a === 1) { // Just the sum of the coefficients. result = 0; for (const coefficient of coefficients) { result ^= coefficient; } return result; } [result] = coefficients; const field = this.#field; const { length } = coefficients; for (let i = 1; i < length; i++) { result = field.multiply(a, result) ^ coefficients[i]; } return result; } multiply(other) { const field = this.#field; const coefficients = this.#coefficients; const { length } = coefficients; if (other instanceof Polynomial) { if (this.isZero() || other.isZero()) { return field.zero; } const otherCoefficients = other.#coefficients; const otherLength = otherCoefficients.length; const product = new Int32Array(length + otherLength - 1); for (let i = 0; i < length; i++) { const coefficient = coefficients[i]; for (let j = 0; j < otherLength; j++) { product[i + j] ^= field.multiply(coefficient, otherCoefficients[j]); } } return new Polynomial(field, product); } if (other === 0) { return field.zero; } if (other === 1) { return this; } const product = new Int32Array(length); for (let i = 0; i < length; i++) { product[i] = field.multiply(coefficients[i], other); } return new Polynomial(field, product); } multiplyByMonomial(degree, coefficient) { const field = this.#field; if (coefficient === 0) { return field.zero; } const coefficients = this.#coefficients; const { length } = coefficients; const product = new Int32Array(length + degree); for (let i = 0; i < length; i++) { product[i] = field.multiply(coefficients[i], coefficient); } return new Polynomial(field, product); } addOrSubtract(other) { if (this.isZero()) { return other; } if (other.isZero()) { return this; } let largerCoefficients = other.#coefficients; let largerLength = largerCoefficients.length; let smallerCoefficients = this.#coefficients; let smallerLength = smallerCoefficients.length; if (largerLength < smallerLength) { [largerLength, smallerLength] = [smallerLength, largerLength]; [largerCoefficients, smallerCoefficients] = [smallerCoefficients, largerCoefficients]; } // Diff index offset. const offset = largerLength - smallerLength; const coefficients = new Int32Array(largerLength); // Copy high-order terms only found in higher-degree polynomial's coefficients. coefficients.set(largerCoefficients.subarray(0, offset)); for (let i = offset; i < largerLength; i++) { coefficients[i] = smallerCoefficients[i - offset] ^ largerCoefficients[i]; } return new Polynomial(this.#field, coefficients); } divide(other) { const field = this.#field; let quotient = field.zero; let remainder = this; const denominatorLeadingTerm = other.getCoefficient(other.getDegree()); const invertDenominatorLeadingTerm = field.invert(denominatorLeadingTerm); while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { const remainderDegree = remainder.getDegree(); const degreeDiff = remainderDegree - other.getDegree(); const scale = field.multiply(remainder.getCoefficient(remainderDegree), invertDenominatorLeadingTerm); const term = other.multiplyByMonomial(degreeDiff, scale); const iterationQuotient = field.buildPolynomial(degreeDiff, scale); quotient = quotient.addOrSubtract(iterationQuotient); remainder = remainder.addOrSubtract(term); } return [quotient, remainder]; } } exports.Polynomial = Polynomial;