Skip to content

Commit

Permalink
Implement more vector operations for monzos
Browse files Browse the repository at this point in the history
  • Loading branch information
frostburn committed Jan 11, 2023
1 parent ebb5548 commit 4d4bf99
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
iteratedEuclid,
lcm,
mmod,
norm,
PRIMES,
valueToCents,
} from '../index';
Expand Down Expand Up @@ -224,3 +225,18 @@ describe('Value clamper', () => {
expect(clamped).toBe(12800);
});
});

describe('Norm', () => {
it('calculates an euclidean norm (float32)', () => {
const a = new Float32Array([-3, 4]);
expect(norm(a)).toBeCloseTo(5);
});
it('calculates a taxicab norm (int8)', () => {
const a = new Int8Array([3, -4]);
expect(norm(a, 'taxicab')).toBeCloseTo(7);
});
it('calculates a max norm (number[])', () => {
const a = [-3, -4];
expect(norm(a, 'maximum')).toBeCloseTo(4);
});
});
26 changes: 26 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,3 +540,29 @@ export function dot(a: NumberArray, b: NumberArray): number {
}
return result;
}

/**
* Calculate the norm (vector length) of an array of real numbers.
* @param array The array to measure.
* @param type Type of measurement.
* @returns The length of the vector.
*/
export function norm(
array: NumberArray,
type: 'euclidean' | 'taxicab' | 'maximum' = 'euclidean'
) {
let result = 0;
for (let i = 0; i < array.length; ++i) {
if (type === 'taxicab') {
result += Math.abs(array[i]);
} else if (type === 'maximum') {
result = Math.max(result, Math.abs(array[i]));
} else {
result += array[i] * array[i];
}
}
if (type === 'euclidean') {
return Math.sqrt(result);
}
return result;
}
63 changes: 63 additions & 0 deletions src/monzo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,69 @@ export function sub(a: Monzo, b: Monzo): Monzo {
return result;
}

/**
* Scale a monzo by a scalar.
* @param monzo The monzo to scale.
* @param amount The amount to scale by.
* @returns The scalar multiple.
*/
export function scale(monzo: Monzo, amount: number) {
return monzo.map(component => component * amount);
}

/**
* Multiply two monzos component-wise.
* @param monzo The first monzo.
* @param weights The second monzo. Missing values interpreted as 1 (no change).
* @returns The first monzo weighted by the second.
*/
export function applyWeights(monzo: Monzo, weights: Monzo) {
const result = [...monzo];
for (let i = 0; i < Math.min(monzo.length, weights.length); ++i) {
result[i] *= weights[i];
}
return result;
}

/**
* Accumulate a monzo into the first one.
* @param target The monzo to accumulate into.
* @param source The monzo to add.
* @returns The (modified) target monzo.
*/
export function accumulate(target: Monzo, source: Monzo) {
for (let i = 0; i < Math.min(target.length, source.length); ++i) {
target[i] += source[i];
}
return target;
}

/**
* Decumulate a monzo into the first one.
* @param target The monzo to decumulate into.
* @param source The monzo to subtract.
* @returns The (modified) target monzo.
*/
export function decumulate(target: Monzo, source: Monzo) {
for (let i = 0; i < Math.min(target.length, source.length); ++i) {
target[i] -= source[i];
}
return target;
}

/**
* Rescale a monzo by a scalar.
* @param target The monzo to rescale.
* @param amount The amount to scale by.
* @returns The (modified) target monzo.
*/
export function rescale(target: Monzo, amount: number) {
for (let i = 0; i < target.length; ++i) {
target[i] *= amount;
}
return target;
}

/**
* Extract the exponents of the prime factors of a rational number.
* @param n Rational number to convert to a monzo.
Expand Down

0 comments on commit 4d4bf99

Please sign in to comment.