dsp-collection
Version:
A collection of JavaScript modules for digital signal processing (written in TypeScript)
227 lines • 6.24 kB
JavaScript
import MutableComplex from "./MutableComplex.js";
export function evaluateReal(a, x) {
if (a.length == 0) {
throw new Error("Zero length array.");
}
const n = a.length - 1;
let r = a[n];
for (let i = n - 1; i >= 0; i--) {
r *= x;
r += a[i];
}
return r;
}
export function evaluateComplex(a, x) {
if (a.length == 0) {
throw new Error("Zero length array.");
}
const n = a.length - 1;
const r = new MutableComplex(a[n]);
for (let i = n - 1; i >= 0; i--) {
r.mulBy(x);
r.addRealTo(a[i]);
}
return r;
}
export function expand(zeros) {
const n = zeros.length;
if (n == 0) {
return Float64Array.of(1);
}
let a = Float64Array.of(-zeros[0], 1);
for (let i = 1; i < n; i++) {
const a2 = Float64Array.of(-zeros[i], 1);
a = multiply(a, a2);
}
return a;
}
export function compareEqual(a1, a2, eps = 0) {
const n1 = a1.length - 1;
const n2 = a2.length - 1;
const n = Math.max(n1, n2);
for (let i = 0; i <= n; i++) {
const v1 = (i <= n1) ? a1[i] : 0;
const v2 = (i <= n2) ? a2[i] : 0;
if (Math.abs(v1 - v2) > eps) {
return false;
}
}
return true;
}
export function add(a1, a2, eps = 0) {
const n1 = a1.length - 1;
const n2 = a2.length - 1;
const n3 = Math.max(n1, n2);
const a3 = new Float64Array(n3 + 1);
for (let i = 0; i <= n3; i++) {
const v1 = (i <= n1) ? a1[i] : 0;
const v2 = (i <= n2) ? a2[i] : 0;
a3[i] = v1 + v2;
}
return trim(a3, eps);
}
export function multiply(a1, a2, eps = 0) {
if (a1.length == 0 || a2.length == 0) {
throw new Error("Zero length arrays.");
}
if (a1.length == 1 && a1[0] == 0 || a2.length == 1 && a2[0] == 0) {
return Float64Array.of(0);
}
const n1 = a1.length - 1;
const n2 = a2.length - 1;
const n3 = n1 + n2;
const a3 = new Float64Array(n3 + 1);
for (let i = 0; i <= n3; i++) {
let t = 0;
const p1 = Math.max(0, i - n2);
const p2 = Math.min(n1, i);
for (let j = p1; j <= p2; j++) {
t += a1[j] * a2[i - j];
}
a3[i] = t;
}
return trim(a3, eps);
}
export function divide(a1r, a2r, eps = 0) {
if (a1r.length == 0 || a2r.length == 0) {
throw new Error("Zero length arrays.");
}
const a1 = trim(a1r, eps);
const a2 = trim(a2r, eps);
if (a2.length == 1) {
if (a2[0] == 0) {
throw new Error("Polynomial division by zero.");
}
if (a2[0] == 1) {
return [Float64Array.from(a1), Float64Array.of(0)];
}
return [divByReal(a1, a2[0]), Float64Array.of(0)];
}
const n1 = a1.length - 1;
const n2 = a2.length - 1;
if (n1 < n2) {
return [Float64Array.of(0), Float64Array.from(a1)];
}
const a = Float64Array.from(a1);
const lc2 = a2[n2];
for (let i = n1 - n2; i >= 0; i--) {
const r = a[n2 + i] / lc2;
a[n2 + i] = r;
for (let j = 0; j < n2; ++j) {
a[i + j] -= r * a2[j];
}
}
const quotient = trim(a.subarray(n2), eps);
const remainder = trim(a.subarray(0, n2), eps);
return [quotient, remainder];
}
export function gcd(a1, a2, eps = 0) {
let r1 = trim(a1, eps);
let r2 = trim(a2, eps);
makeMonic(r1);
makeMonic(r2);
if (r1.length < r2.length) {
[r1, r2] = [r2, r1];
}
while (true) {
if (r2.length < 2) {
return Float64Array.of(1);
}
const r = divide(r1, r2, eps)[1];
if (r.length == 1 && r[0] == 0) {
return r2;
}
makeMonic(r);
r1 = r2;
r2 = r;
}
}
function trim(a, eps = 0) {
if (a.length == 0) {
throw new Error("Zero length array.");
}
if (Math.abs(a[a.length - 1]) > eps) {
return Float64Array.from(a);
}
let len = a.length - 1;
while (len > 0 && Math.abs(a[len - 1]) <= eps) {
len--;
}
if (len == 0) {
return Float64Array.of(0);
}
const a2 = new Float64Array(len);
for (let i = 0; i < len; i++) {
a2[i] = a[i];
}
return a2;
}
function makeMonic(a) {
const len = a.length;
if (len == 0) {
throw new Error("Zero length array.");
}
const lc = a[len - 1];
if (lc == 1) {
return;
}
if (lc == 0) {
throw new Error("Leading coefficient is zero.");
}
a[len - 1] = 1;
for (let i = 0; i < len - 1; i++) {
a[i] /= lc;
}
}
function divByReal(a, b) {
const a2 = new Float64Array(a.length);
for (let i = 0; i < a.length; i++) {
a2[i] = a[i] / b;
}
return a2;
}
function divByRealInPlace(a, b) {
for (let i = 0; i < a.length; i++) {
a[i] /= b;
}
}
export function evaluateFractionComplex(f, x) {
const v1 = evaluateComplex(f[0], x);
const v2 = evaluateComplex(f[1], x);
return v1.div(v2);
}
export function addFractions(f1, f2, eps = 0) {
if (compareEqual(f1[1], f2[1], eps)) {
return [add(f1[0], f2[0], eps), Float64Array.from(f1[1])];
}
const g = gcd(f1[1], f2[1], eps);
if (g.length == 1 && g[0] == 1) {
const top = add(multiply(f1[0], f2[1], eps), multiply(f2[0], f1[1], eps));
const bottom = multiply(f1[1], f2[1], eps);
return [top, bottom];
}
const q1 = divide(f1[1], g, eps);
const q2 = divide(f2[1], g, eps);
const m1 = q1[0];
const m2 = q2[0];
const top = add(multiply(f1[0], m2, eps), multiply(f2[0], m1, eps));
const bottom = multiply(f1[1], m2, eps);
return [top, bottom];
}
export function multiplyFractions(f1, f2, eps = 0) {
const top = multiply(f1[0], f2[0], eps);
const bottom = multiply(f1[1], f2[1], eps);
return [top, bottom];
}
export function normalizeFraction(f, eps = 0) {
const top = trim(f[0], eps);
const bottom = trim(f[1], eps);
const lc = bottom[bottom.length - 1];
if (lc == 0) {
throw new Error("Fraction denominator is zero.");
}
divByRealInPlace(top, lc);
divByRealInPlace(bottom, lc);
return [top, bottom];
}
//# sourceMappingURL=PolyReal.js.map