Skip to content

Instantly share code, notes, and snippets.

@robert-nix
Forked from Zemnmez/math.test.ts
Last active August 26, 2020 13:29
Show Gist options
  • Save robert-nix/8fd923d196a5b9d83ce0a188b3d148d2 to your computer and use it in GitHub Desktop.
Save robert-nix/8fd923d196a5b9d83ce0a188b3d148d2 to your computer and use it in GitHub Desktop.
import * as matrix from './matrix';
import * as vec from './vec';
test.each([
[
matrix.as<3, 2>([
[1,2,3],
[4,5,6]
] as const),
matrix.as<2, 3>([
[1, 4],
[2, 5],
[3, 6]
] as const)
],
[
matrix.as<3, 1>([
[1,2,3]
] as const),
matrix.as<1, 3>([
[1],
[2],
[3]
] as const)
]
] as const)("matrix.transpose(%p) => %p", (a, b) => {
expect(matrix.transpose(a)).toEqual(b)
})
test.each([
[ [1,2,3], [3,2,1], 10]
])("vec.dot(%p, %p) => %p", (a, b, o) => {
expect(vec.dot(a, b)).toEqual(o)
});
test.each([
[
matrix.as<2,3>([
[1, 2],
[1, 4],
[6, 5]
] as const),
0,
[1, 1, 6]
]
])("matrix.col(%p, %p) => %p", (a, b, o) => {
expect([...matrix.col(a, b)]).toEqual(o)
})
test.each([
[
matrix.as<2,3>([
[1, 2],
[1, 4],
[6, 5]
] as const),
0,
[1, 2]
]
])("matrix.row(%p, %p) => %p", (a, b, o) => {
expect([...matrix.row(a, b)]).toEqual(o)
})
test.each([
[
matrix.as<2, 1>([
[6, 9]
] as const),
matrix.as<2, 2>([
[0, 1],
[1, 0]
] as const),
matrix.as<2, 1>([
[9, 6]
] as const)
],
[
matrix.as<2,2>([
[1, 0],
[0, 1]
]as const),
matrix.as<2, 2>([
[2, 4],
[10, 9]
] as const),
matrix.as<2,2>([
[2, 4],
[10, 9]
] as const)
]
])("matrix.mul(%p, %p) => %p", (a, b, o) => {
expect(matrix.mul(a, b)).toEqual(o)
})
import * as vec from './vec';
import { Vector } from './vec';
// Matrix is a J-length vector of I-length vectors.
// The inner vectors are the rows, so the number of columns is I and the number of rows is J.
export interface Matrix<I extends number = number, J extends number = number, T = number> extends Vector<J, Vector<I, T>> {
}
export const as:
<I extends number = number, J extends number = number, T = number>(v: (readonly((readonly T[]) & { length: I })[]) & { length: J }) => Matrix<I, J, T>
=
v => v as any
;
export const add:
<I extends number, J extends number>(m1: Matrix<I, J>, m2: Matrix<I, J>) => Matrix<I,J>
=
<I extends number, J extends number>(m1: Matrix<I, J>, m2: Matrix<I, J>) =>
vec.map(m1, (row, i) => vec.add(row, m2[i]))
;
export const row:
<I extends number, J extends number, T>(v: Matrix<I, J, T>, r: number) => Iterable<T>
=
function*(v, i) {
const a = v[i];
if (!a) return;
for (let i = 0; i < a.length; i++) yield a[i];
}
;
export const col:
<I extends number, J extends number, T>(v: Matrix<I, J, T>, i: number) => Iterable<T>
=
function*(v, i) {
const [, jsize] = size(v);
for (let j = 0; j < jsize; j++) yield v[j][i];
}
;
// Since I1 = #cols, J1 = #rows, the result is a J1 rows x I2 cols matrix
export const mul:
<I1 extends number, J1 extends number,
I2 extends number, J2 extends number>(m1: Matrix<I1, J1>, m2: Matrix<I2, J2>) =>
Matrix<I2, J1>
=
<I1 extends number, J1 extends number, I2 extends number, J2 extends number>(
m1: Matrix<I1, J1>,
m2: Matrix<I2, J2>) => {
const [i1, j1] = size(m1);
const [i2, j2] = size(m2);
return vec.map(vec.New<J1>(j1), (_, i) =>
vec.map(vec.New<I2>(i2), (_, j) => vec.dot(row(m1, i), col(m2, j))));
}
;
export const size:
<I extends number, J extends number>(m: Matrix<I, J, any>) =>
J extends 0? [undefined, J]: [I, J]
=
m => [m[0]?.length, m.length] as any
;
export const transpose:
<I extends number, J extends number>(m: Matrix<I, J>) => Matrix<J, I>
=
<I extends number, J extends number>(m: Matrix<I, J>) => {
const [i, j] = size(m)
const rows = vec.New<I>(i);
return vec.map(rows, (_, rj) => vec.map(vec.New<J>(j), (__, vi) => m[vi][rj]))
}
;
interface ReadonlyArrayOfLength<T, I extends number> extends ReadonlyArray<T> {
length: I
}
export interface Vector<I extends number = number, T = number> extends ReadonlyArrayOfLength<T, I> {
length: I
}
type mapFn<I extends number, T> = <U>(callbackFn: (value: T, index: number, array: Vector<I, T>) => U, thisArg?: any) =>
Vector<I, U>;
export const map:
<I extends number, T, U>(
vec: Vector<I, T>,
callbackFn: (value: T, index: number, array: Vector<I, T>) => U) =>
Vector<I, U>
=
(vec, c) => (vec.map as mapFn<any, any>)(c)
;
export const imap:
<T, U>(
v: Iterable<T>,
f: (v: T, i: number) => U) => Iterable<U>
=
function*(v, f) {
let i = 0;
for (const l of v) {
yield f(l, i)
i++
}
}
;
export const as:
<T, L extends number>(v: (readonly T[]) & { length: L }) => Vector<L, T>
=
v => v as any
;
export const New:
<N extends number>(n: number) => Vector<N, undefined>
=
n => [...Array(n)] as any as Vector<any, undefined>
;
export const add:
<I extends number>(v1: Vector<I>, v2: Vector<I>) => Vector<I>
=
(v1, v2) => map(v1, (v, i) => v + v2[i])
;
export const mul:
<I extends number>(v1: number, v2: Vector<I>) => Vector<I>
=
(v1, v2) => map(v2, (v, i) => v * v1)
;
export const dot:
(v1: Iterable<number>, v2: Iterable<number>) =>
number
=
(v1, v2) =>
sum(imap(zip(v1, v2, 0 as const), ([a = 0 as const, b = 0 as const]) => a * b))
;
export const sum:
(v1: Iterable<number>) => number
=
v => [...v].reduce((a, c) => a+c, 0)
;
export const zip:
<T1, T2, T3 >(v1: Iterable<T1>, v2: Iterable<T2>, fb: T3) => Iterable<[T1 | T3, T2 | T3]>
=
function*(v1, v2, fb) {
const [a, b] = [v1[Symbol.iterator](), v2[Symbol.iterator]()];
for (let i = 0; ; i++) {
const [ar, br] = [a.next(), b.next()];
const left = ar.done? fb: ar.value;
const right = br.done? fb: br.value;
if (ar.done&& br.done) break;
yield [left, right]
}
}
;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment