refactor: Remove the _addTo method from BinaryTree and TreeMultiMap.

feat: Reimplement Matrix.
docs: Use typedoc.json configuration to only output class documentation.
This commit is contained in:
Revone 2023-12-22 19:59:38 +08:00
parent dc6ef95f78
commit 2247316d16
16 changed files with 1313 additions and 1394 deletions

View file

@ -28,7 +28,7 @@ anymore! JavaScript and TypeScript now have [data-structure-typed]().**`Benchmar
### We provide data structures that are not available in JS/TS
Heap, Binary Tree, RedBlack Tree, Linked List, Deque, Trie, Directed Graph, Undirected Graph, BST, AVL Tree, Priority Queue, Queue, Tree Multiset, Linked List.
Heap, Binary Tree, RedBlack Tree, Linked List, Deque, Trie, Directed Graph, Undirected Graph, BST, AVL Tree, Priority Queue, Queue, Tree Multiset.
### Performance surpasses that of native JS/TS
@ -1033,5 +1033,3 @@ avl2.print();
</div>
[//]: # (No deletion!!! End of Replace Section)

View file

@ -25,7 +25,7 @@ JavaScript和TypeScript的数据结构。
### 提供了JS/TS中没有的数据结构
Heap, Binary Tree, RedBlack Tree, Linked List, Deque, Trie, Directed Graph, Undirected Graph, BST, AVL Tree, Priority Queue, Queue, Tree Multiset, Linked List.
Heap, Binary Tree, RedBlack Tree, Linked List, Deque, Trie, Directed Graph, Undirected Graph, BST, AVL Tree, Priority Queue, Queue, Tree Multiset.
### 性能超越原生JS/TS

701
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -14,11 +14,12 @@
}
},
"scripts": {
"build": "npm run build:mjs && npm run build:cjs && npm run build:umd && npm run build:docs",
"build": "npm run build:mjs && npm run build:cjs && npm run build:umd && npm run build:docs-class",
"build:mjs": "rm -rf dist/mjs && tsc -p tsconfig-mjs.json",
"build:cjs": "rm -rf dist/cjs && tsc -p tsconfig-cjs.json",
"build:umd": "tsup",
"build:docs": "typedoc --out docs ./src",
"build:docs-class": "typedoc --out docs ./src/data-structures",
"test:unit": "jest --runInBand",
"test": "npm run test:unit",
"test:integration": "npm run update:subs && jest --config jest.integration.config.js",
@ -87,7 +88,7 @@
"ts-loader": "^9.4.4",
"ts-node": "^10.9.1",
"tsup": "^7.2.0",
"typedoc": "^0.25.1",
"typedoc": "^0.25.4",
"typescript": "^5.3.2"
},
"keywords": [

View file

@ -2032,43 +2032,6 @@ export class BinaryTree<
return newNode;
}
/**
* The function `_addTo` adds a new node to a binary tree if there is an available position.
* @param {N | null | undefined} newNode - The `newNode` parameter represents the node that you want to add to
* the binary tree. It can be either a node object or `null`.
* @param {N} parent - The `parent` parameter represents the parent node to which the new node will
* be added as a child.
* @returns either the left or right child node of the parent node, depending on which child is
* available for adding the new node. If a new node is added, the function also updates the size of
* the binary tree. If neither the left nor right child is available, the function returns undefined.
* If the parent node is null, the function also returns undefined.
*/
protected _addTo(newNode: N | null | undefined, parent: BTNKeyOrNode<K, N>): N | null | undefined {
if (this.isNotNodeInstance(parent)) parent = this.getNode(parent);
if (parent) {
// When all leaf nodes are null, it will no longer be possible to add new entity nodes to this binary tree.
// In this scenario, null nodes serve as "sentinel nodes," "virtual nodes," or "placeholder nodes."
if (parent.left === undefined) {
parent.left = newNode;
if (newNode) {
this._size = this.size + 1;
}
return parent.left;
} else if (parent.right === undefined) {
parent.right = newNode;
if (newNode) {
this._size = this.size + 1;
}
return parent.right;
} else {
return;
}
} else {
return;
}
}
/**
* The function sets the root property of an object to a given value, and if the value is not null,
* it also sets the parent property of the value to undefined.

View file

@ -361,47 +361,6 @@ export class TreeMultimap<
return cloned;
}
/**
* Time Complexity: O(1) - constant time, as it performs basic pointer assignments.
* Space Complexity: O(1) - constant space, as it only uses a constant amount of memory.
*
* The function adds a new node to a binary tree, either as the left child or the right child of a
* given parent node.
* @param {N | undefined} newNode - The `newNode` parameter represents the node that needs to be
* added to the binary tree. It can be of type `N` (which represents a node in the binary tree) or
* `undefined` if there is no node to add.
* @param {K | N | undefined} parent - The `parent` parameter represents the parent node to
* which the new node will be added as a child. It can be either a node object (`N`) or a key value
* (`K`).
* @returns The method `_addTo` returns either the `parent.left` or `parent.right` node that was
* added, or `undefined` if no node was added.
*/
protected override _addTo(newNode: N | undefined, parent: BSTNKeyOrNode<K, N>): N | undefined {
parent = this.ensureNode(parent);
if (parent) {
if (parent.left === undefined) {
parent.left = newNode;
if (newNode !== undefined) {
this._size = this.size + 1;
this._count += newNode.count;
}
return parent.left;
} else if (parent.right === undefined) {
parent.right = newNode;
if (newNode !== undefined) {
this._size = this.size + 1;
this._count += newNode.count;
}
return parent.right;
} else {
return;
}
} else {
return;
}
}
/**
* The `_swapProperties` function swaps the key, value, count, and height properties between two nodes.
* @param {K | N | undefined} srcNode - The `srcNode` parameter represents the source node from

View file

@ -1,4 +1,2 @@
export * from './matrix';
export * from './vector2d';
export * from './matrix2d';
export * from './navigator';

View file

@ -5,23 +5,452 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
// todo need to be improved
export class MatrixNTI2D<V = any> {
protected readonly _matrix: Array<Array<V>>;
export class Matrix {
/**
* The constructor function initializes a matrix object with the provided data and options, or with
* default values if no options are provided.
* @param {number[][]} data - A 2D array of numbers representing the data for the matrix.
* @param [options] - The `options` parameter is an optional object that can contain the following
* properties:
*/
constructor(
data: number[][],
options?: {
rows?: number;
cols?: number;
addFn?: (a: number, b: number) => any;
subtractFn?: (a: number, b: number) => any;
multiplyFn?: (a: number, b: number) => any;
}
) {
if (options) {
const { rows, cols, addFn, subtractFn, multiplyFn } = options;
if (typeof rows === 'number' && rows > 0) this._rows = rows;
else this._rows = data.length;
if (typeof cols === 'number' && cols > 0) this._cols = cols;
else this._cols = data[0]?.length || 0;
if (addFn) this._addFn = addFn;
if (subtractFn) this._subtractFn = subtractFn;
if (multiplyFn) this._multiplyFn = multiplyFn;
} else {
this._rows = data.length;
this._cols = data[0]?.length ?? 0;
}
if (data.length > 0) {
this._data = data;
} else {
this._data = [];
for (let i = 0; i < this.rows; i++) {
this._data[i] = new Array(this.cols).fill(0);
}
}
}
protected _rows: number = 0;
get rows(): number {
return this._rows;
}
protected _cols: number = 0;
get cols(): number {
return this._cols;
}
protected _data: number[][];
get data(): number[][] {
return this._data;
}
get addFn() {
return this._addFn;
}
get subtractFn() {
return this._subtractFn;
}
get multiplyFn() {
return this._multiplyFn;
}
/**
* The constructor creates a matrix with the specified number of rows and columns, and initializes all elements to a
* given initial value or 0 if not provided.
* @param options - An object containing the following properties:
* The `get` function returns the value at the specified row and column index if it is a valid index.
* @param {number} row - The `row` parameter represents the row index of the element you want to
* retrieve from the data array.
* @param {number} col - The parameter "col" represents the column number of the element you want to
* retrieve from the data array.
* @returns The `get` function returns a number if the provided row and column indices are valid.
* Otherwise, it returns `undefined`.
*/
constructor(options: { row: number; col: number; initialVal?: V }) {
const { row, col, initialVal } = options;
this._matrix = new Array(row).fill(undefined).map(() => new Array(col).fill(initialVal || 0));
get(row: number, col: number): number | undefined {
if (this.isValidIndex(row, col)) {
return this.data[row][col];
}
}
/* The `toArray` method returns the matrix as a two-dimensional array. It converts the internal representation of the
matrix, which is an array of arrays, into a format that is more commonly used in JavaScript. */
toArray(): Array<Array<V>> {
return this._matrix;
/**
* The set function updates the value at a specified row and column in a two-dimensional array.
* @param {number} row - The "row" parameter represents the row index of the element in a
* two-dimensional array or matrix. It specifies the row where the value will be set.
* @param {number} col - The "col" parameter represents the column index of the element in a
* two-dimensional array.
* @param {number} value - The value parameter represents the number that you want to set at the
* specified row and column in the data array.
* @returns a boolean value. It returns true if the index (row, col) is valid and the value is
* successfully set in the data array. It returns false if the index is invalid and the value is not
* set.
*/
set(row: number, col: number, value: number): boolean {
if (this.isValidIndex(row, col)) {
this.data[row][col] = value;
return true;
}
return false;
}
/**
* The function checks if the dimensions of the given matrix match the dimensions of the current
* matrix.
* @param {Matrix} matrix - The parameter `matrix` is of type `Matrix`.
* @returns a boolean value.
*/
isMatchForCalculate(matrix: Matrix): boolean {
return this.rows === matrix.rows && this.cols === matrix.cols;
}
/**
* The `add` function adds two matrices together, returning a new matrix with the result.
* @param {Matrix} matrix - The `matrix` parameter is an instance of the `Matrix` class.
* @returns The `add` method returns a new `Matrix` object that represents the result of adding the
* current matrix with the provided `matrix` parameter.
*/
add(matrix: Matrix): Matrix | undefined {
if (!this.isMatchForCalculate(matrix)) {
throw new Error('Matrix dimensions must match for addition.');
}
const resultData: number[][] = [];
for (let i = 0; i < this.rows; i++) {
resultData[i] = [];
for (let j = 0; j < this.cols; j++) {
const a = this.get(i, j),
b = matrix.get(i, j);
if (a !== undefined && b !== undefined) {
const added = this._addFn(a, b);
if (added) {
resultData[i][j] = added;
}
}
}
}
return new Matrix(resultData, {
rows: this.rows,
cols: this.cols,
addFn: this.addFn,
subtractFn: this.subtractFn,
multiplyFn: this.multiplyFn
});
}
/**
* The `subtract` function performs element-wise subtraction between two matrices and returns a new
* matrix with the result.
* @param {Matrix} matrix - The `matrix` parameter is an instance of the `Matrix` class. It
* represents the matrix that you want to subtract from the current matrix.
* @returns a new Matrix object with the result of the subtraction operation.
*/
subtract(matrix: Matrix): Matrix | undefined {
if (!this.isMatchForCalculate(matrix)) {
throw new Error('Matrix dimensions must match for subtraction.');
}
const resultData: number[][] = [];
for (let i = 0; i < this.rows; i++) {
resultData[i] = [];
for (let j = 0; j < this.cols; j++) {
const a = this.get(i, j),
b = matrix.get(i, j);
if (a !== undefined && b !== undefined) {
const subtracted = this._subtractFn(a, b);
if (subtracted) {
resultData[i][j] = subtracted;
}
}
}
}
return new Matrix(resultData, {
rows: this.rows,
cols: this.cols,
addFn: this.addFn,
subtractFn: this.subtractFn,
multiplyFn: this.multiplyFn
});
}
/**
* The `multiply` function performs matrix multiplication between two matrices and returns the result
* as a new matrix.
* @param {Matrix} matrix - The `matrix` parameter is an instance of the `Matrix` class.
* @returns a new Matrix object.
*/
multiply(matrix: Matrix): Matrix | undefined {
if (this.cols !== matrix.rows) {
throw new Error('Matrix dimensions must be compatible for multiplication (A.cols = B.rows).');
}
const resultData: number[][] = [];
for (let i = 0; i < this.rows; i++) {
resultData[i] = [];
for (let j = 0; j < matrix.cols; j++) {
let sum: number | undefined;
for (let k = 0; k < this.cols; k++) {
const a = this.get(i, k),
b = matrix.get(k, j);
if (a !== undefined && b !== undefined) {
const multiplied = this.multiplyFn(a, b);
if (multiplied !== undefined) {
sum = this.addFn(sum, multiplied);
}
}
}
if (sum !== undefined) resultData[i][j] = sum;
}
}
return new Matrix(resultData, {
rows: this.rows,
cols: matrix.cols,
addFn: this.addFn,
subtractFn: this.subtractFn,
multiplyFn: this.multiplyFn
});
}
/**
* The transpose function takes a matrix and returns a new matrix that is the transpose of the
* original matrix.
* @returns The transpose() function returns a new Matrix object with the transposed data.
*/
transpose(): Matrix {
if (this.data.some(row => row.length !== this.rows)) {
throw new Error('Matrix must be rectangular for transposition.');
}
const resultData: number[][] = [];
for (let j = 0; j < this.cols; j++) {
resultData[j] = [];
for (let i = 0; i < this.rows; i++) {
const trans = this.get(i, j);
if (trans !== undefined) resultData[j][i] = trans;
}
}
return new Matrix(resultData, {
rows: this.cols,
cols: this.rows,
addFn: this.addFn,
subtractFn: this.subtractFn,
multiplyFn: this.multiplyFn
});
}
/**
* The `inverse` function calculates the inverse of a square matrix using Gaussian elimination.
* @returns a Matrix object, which represents the inverse of the original matrix.
*/
inverse(): Matrix | undefined {
// Check if the matrix is square
if (this.rows !== this.cols) {
throw new Error('Matrix must be square for inversion.');
}
// Create an augmented matrix [this | I]
const augmentedMatrixData: number[][] = [];
for (let i = 0; i < this.rows; i++) {
augmentedMatrixData[i] = this.data[i].slice(); // Copy the original matrix
for (let j = 0; j < this.cols; j++) {
augmentedMatrixData[i][this.cols + j] = i === j ? 1 : 0; // Append the identity matrix
}
}
const augmentedMatrix = new Matrix(augmentedMatrixData, {
rows: this.rows,
cols: this.cols * 2,
addFn: this.addFn,
subtractFn: this.subtractFn,
multiplyFn: this.multiplyFn
});
// Apply Gaussian elimination to transform the left half into the identity matrix
for (let i = 0; i < this.rows; i++) {
// Find pivot
let pivotRow = i;
while (pivotRow < this.rows && augmentedMatrix.get(pivotRow, i) === 0) {
pivotRow++;
}
if (pivotRow === this.rows) {
// Matrix is singular, and its inverse does not exist
throw new Error('Matrix is singular, and its inverse does not exist.');
}
// Swap rows to make the pivot the current row
augmentedMatrix._swapRows(i, pivotRow);
// Scale the pivot row to make the pivot element 1
const pivotElement = augmentedMatrix.get(i, i) ?? 1;
if (pivotElement === 0) {
// Handle division by zero
throw new Error('Matrix is singular, and its inverse does not exist (division by zero).');
}
augmentedMatrix._scaleRow(i, 1 / pivotElement);
// Eliminate other rows to make elements in the current column zero
for (let j = 0; j < this.rows; j++) {
if (j !== i) {
let factor = augmentedMatrix.get(j, i);
if (factor === undefined) factor = 0;
augmentedMatrix._addScaledRow(j, i, -factor);
}
}
}
// Extract the right half of the augmented matrix as the inverse
const inverseData: number[][] = [];
for (let i = 0; i < this.rows; i++) {
inverseData[i] = augmentedMatrix.data[i].slice(this.cols);
}
return new Matrix(inverseData, {
rows: this.rows,
cols: this.cols,
addFn: this.addFn,
subtractFn: this.subtractFn,
multiplyFn: this.multiplyFn
});
}
/**
* The dot function calculates the dot product of two matrices and returns a new matrix.
* @param {Matrix} matrix - The `matrix` parameter is an instance of the `Matrix` class.
* @returns a new Matrix object.
*/
dot(matrix: Matrix): Matrix | undefined {
if (this.cols !== matrix.rows) {
throw new Error(
'Number of columns in the first matrix must be equal to the number of rows in the second matrix for dot product.'
);
}
const resultData: number[][] = [];
for (let i = 0; i < this.rows; i++) {
resultData[i] = [];
for (let j = 0; j < matrix.cols; j++) {
let sum: number | undefined;
for (let k = 0; k < this.cols; k++) {
const a = this.get(i, k),
b = matrix.get(k, j);
if (a !== undefined && b !== undefined) {
const multiplied = this.multiplyFn(a, b);
if (multiplied !== undefined) {
sum = this.addFn(sum, multiplied);
}
}
}
if (sum !== undefined) resultData[i][j] = sum;
}
}
return new Matrix(resultData, {
rows: this.rows,
cols: matrix.cols,
addFn: this.addFn,
subtractFn: this.subtractFn,
multiplyFn: this.multiplyFn
});
}
protected _addFn(a: number | undefined, b: number): number | undefined {
if (a === undefined) return b;
return a + b;
}
protected _subtractFn(a: number, b: number) {
return a - b;
}
protected _multiplyFn(a: number, b: number) {
return a * b;
}
/**
* The function checks if a given row and column index is valid within a specified range.
* @param {number} row - The `row` parameter represents the row index of a two-dimensional array or
* matrix. It is a number that indicates the specific row in the matrix.
* @param {number} col - The "col" parameter represents the column index in a two-dimensional array
* or grid. It is used to check if the given column index is valid within the bounds of the grid.
* @returns A boolean value is being returned.
*/
protected isValidIndex(row: number, col: number): boolean {
return row >= 0 && row < this.rows && col >= 0 && col < this.cols;
}
/**
* The function `_swapRows` swaps the positions of two rows in an array.
* @param {number} row1 - The `row1` parameter is the index of the first row that you want to swap.
* @param {number} row2 - The `row2` parameter is the index of the second row that you want to swap
* with the first row.
*/
protected _swapRows(row1: number, row2: number): void {
const temp = this.data[row1];
this.data[row1] = this.data[row2];
this.data[row2] = temp;
}
/**
* The function scales a specific row in a matrix by a given scalar value.
* @param {number} row - The `row` parameter represents the index of the row in the matrix that you
* want to scale. It is a number that indicates the position of the row within the matrix.
* @param {number} scalar - The scalar parameter is a number that is used to multiply each element in
* a specific row of a matrix.
*/
protected _scaleRow(row: number, scalar: number): void {
for (let j = 0; j < this.cols; j++) {
let multiplied = this.multiplyFn(this.data[row][j], scalar);
if (multiplied === undefined) multiplied = 0;
this.data[row][j] = multiplied;
}
}
/**
* The function `_addScaledRow` multiplies a row in a matrix by a scalar value and adds it to another
* row.
* @param {number} targetRow - The targetRow parameter represents the index of the row in which the
* scaled values will be added.
* @param {number} sourceRow - The sourceRow parameter represents the index of the row from which the
* values will be scaled and added to the targetRow.
* @param {number} scalar - The scalar parameter is a number that is used to scale the values in the
* source row before adding them to the target row.
*/
protected _addScaledRow(targetRow: number, sourceRow: number, scalar: number): void {
for (let j = 0; j < this.cols; j++) {
let multiplied = this.multiplyFn(this.data[sourceRow][j], scalar);
if (multiplied === undefined) multiplied = 0;
const scaledValue = multiplied;
let added = this.addFn(this.data[targetRow][j], scaledValue);
if (added === undefined) added = 0;
this.data[targetRow][j] = added;
}
}
}

View file

@ -1,211 +0,0 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import { Vector2D } from './vector2d';
export class Matrix2D {
protected readonly _matrix: number[][];
/**
* The constructor function initializes a Matrix2D object with either a default identity matrix, or a provided matrix
* or Vector2D object.
* @param {number[][] | Vector2D} [value] - The `value` parameter can be either a 2D array of numbers (`number[][]`) or
* an instance of the `Vector2D` class.
*/
constructor(value?: number[][] | Vector2D) {
if (typeof value === 'undefined') {
this._matrix = Matrix2D.identity;
} else if (value instanceof Vector2D) {
this._matrix = Matrix2D.identity;
this._matrix[0][0] = value.x;
this._matrix[1][0] = value.y;
this._matrix[2][0] = value.w;
} else {
this._matrix = value;
}
}
/**
* The function returns a 2D array with three empty arrays.
* @returns An empty 2-dimensional array with 3 empty arrays inside.
*/
static get empty(): number[][] {
return [[], [], []];
}
/**
* The above function returns a 3x3 identity matrix.
* @returns The method is returning a 2-dimensional array of numbers representing the identity matrix.
*/
static get identity(): number[][] {
return [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
];
}
/**
* The function returns a two-dimensional array of numbers.
* @returns The getter method is returning the value of the private variable `_matrix`, which is a two-dimensional
* array of numbers.
*/
get m(): number[][] {
return this._matrix;
}
/**
* The function takes two 2D matrices as input and returns their sum as a new 2D matrix.
* @param {Matrix2D} matrix1 - Matrix2D - The first matrix to be added.
* @param {Matrix2D} matrix2 - The parameter `matrix2` is a Matrix2D object.
* @returns a new instance of the Matrix2D class, which is created using the result array.
*/
static add(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D {
const result = Matrix2D.empty;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
result[i][j] = matrix1.m[i][j] + matrix2.m[i][j];
}
}
return new Matrix2D(result);
}
/**
* The function subtracts two 2D matrices and returns the result as a new Matrix2D object.
* @param {Matrix2D} matrix1 - Matrix2D - The first matrix to subtract from.
* @param {Matrix2D} matrix2 - Matrix2D is a class representing a 2D matrix. It has a property `m` which is a 2D array
* representing the matrix elements.
* @returns a new instance of the Matrix2D class, which is created using the result array.
*/
static subtract(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D {
const result = Matrix2D.empty;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
result[i][j] = matrix1.m[i][j] - matrix2.m[i][j];
}
}
return new Matrix2D(result);
}
/**
* The function multiplies two 2D matrices and returns the result as a new Matrix2D object.
* @param {Matrix2D} matrix1 - A 2D matrix represented by the Matrix2D class.
* @param {Matrix2D} matrix2 - The parameter `matrix2` is a 2D matrix of size 3x3.
* @returns a new instance of the Matrix2D class, created using the result array.
*/
static multiply(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D {
const result = Matrix2D.empty;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
result[i][j] = 0;
for (let k = 0; k < 3; k++) {
result[i][j] += matrix1.m[i][k] * matrix2.m[k][j];
}
}
}
return new Matrix2D(result);
}
/**
* The function multiplies each element of a 2D matrix by a given value and returns the resulting matrix.
* @param {Matrix2D} matrix - The `matrix` parameter is an instance of the `Matrix2D` class, which represents a 2D
* matrix. It contains a property `m` that is a 2D array representing the matrix elements.
* @param {number} value - The `value` parameter is a number that you want to multiply each element of the `matrix` by.
* @returns a new instance of the Matrix2D class, which is created using the result array.
*/
static multiplyByValue(matrix: Matrix2D, value: number): Matrix2D {
const result = Matrix2D.empty;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
result[i][j] = matrix.m[i][j] * value;
}
}
return new Matrix2D(result);
}
/**
* The function multiplies a 2D matrix by a 2D vector and returns the result as a 2D vector.
* @param {Matrix2D} matrix - The parameter "matrix" is of type Matrix2D. It represents a 2-dimensional matrix.
* @param {Vector2D} vector - The "vector" parameter is a 2D vector, represented by an object of type Vector2D.
* @returns a Vector2D.
*/
static multiplyByVector(matrix: Matrix2D, vector: Vector2D): Vector2D {
const resultMatrix = Matrix2D.multiply(matrix, new Matrix2D(vector));
return resultMatrix.toVector();
}
/**
* The function returns a 2D matrix that scales and flips a vector around the center of a given width and height.
* @param {number} width - The width parameter represents the width of the view or the canvas. It is a number that
* specifies the width in pixels or any other unit of measurement.
* @param {number} height - The height parameter represents the height of the view or the canvas. It is used to
* calculate the centerY value, which is the vertical center of the view.
* @returns a Matrix2D object.
*/
static view(width: number, height: number): Matrix2D {
const scaleStep = 1; // Scale every vector * scaleStep
const centerX = width / 2;
const centerY = height / 2;
const flipX = Math.cos(Math.PI); // rotate 180deg / 3.14radian around X-axis
return new Matrix2D([
[scaleStep, 0, centerX],
[0, flipX * scaleStep, centerY],
[0, 0, 1]
]);
}
/**
* The function scales a matrix by a given factor.
* @param {number} factor - The factor parameter is a number that represents the scaling factor by which the matrix
* should be scaled.
* @returns the result of multiplying a new instance of Matrix2D by the given factor.
*/
static scale(factor: number) {
return Matrix2D.multiplyByValue(new Matrix2D(), factor);
}
/**
* The function "rotate" takes an angle in radians and returns a 2D transformation matrix for rotating objects.
* @param {number} radians - The "radians" parameter is the angle in radians by which you want to rotate an object.
* @returns The code is returning a new instance of a Matrix2D object.
*/
static rotate(radians: number) {
const cos = Math.cos(radians);
const sin = Math.sin(radians);
return new Matrix2D([
[cos, -sin, 0],
[sin, cos, 0],
[0, 0, 1]
]);
}
/**
* The translate function takes a 2D vector and returns a 2D matrix that represents a translation transformation.
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D. It represents a 2D vector with components x
* and y, and an optional w component.
* @returns The method is returning a new instance of the Matrix2D class.
*/
static translate(vector: Vector2D): Matrix2D {
return new Matrix2D([
[1, 0, vector.x],
[0, 1, vector.y],
[0, 0, vector.w]
]);
}
/**
* The function "toVector" returns a new Vector2D object with the values from the first and second elements of the
* _matrix array.
* @returns A new instance of the Vector2D class is being returned. The values of the returned vector are taken from
* the first column of the matrix.
*/
toVector(): Vector2D {
return new Vector2D(this._matrix[0][0], this._matrix[1][0]);
}
}

View file

@ -1,315 +0,0 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
export class Vector2D {
constructor(
public x: number = 0,
public y: number = 0,
public w: number = 1 // needed for matrix multiplication
) {
}
/**
* The function checks if the x and y values of a point are both zero.
* @returns A boolean value indicating whether both the x and y properties of the object are equal to 0.
*/
get isZero(): boolean {
return this.x === 0 && this.y === 0;
}
/**
* The above function calculates the length of a vector using the Pythagorean theorem.
* @returns The length of a vector, calculated using the Pythagorean theorem.
*/
get length(): number {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
/**
* The function calculates the square of the length of a vector.
* @returns The method is returning the sum of the squares of the x and y values.
*/
get lengthSq(): number {
return this.x * this.x + this.y * this.y;
}
/**
* The "rounded" function returns a new Vector2D object with the x and y values rounded to the nearest whole number.
* @returns The method is returning a new instance of the Vector2D class with the x and y values rounded to the nearest
* whole number.
*/
get rounded(): Vector2D {
return new Vector2D(Math.round(this.x), Math.round(this.y));
}
/**
* The function "add" takes two Vector2D objects as parameters and returns a new Vector2D object with the sum of their
* x and y components.
* @param {Vector2D} vector1 - The parameter `vector1` is an instance of the `Vector2D` class. It represents a
* 2-dimensional vector with an `x` and `y` component.
* @param {Vector2D} vector2 - The parameter "vector2" is of type Vector2D. It represents a 2-dimensional vector with
* an x and y component.
* @returns The method is returning a new instance of the Vector2D class with the x and y components of the two input
* vectors added together.
*/
static add(vector1: Vector2D, vector2: Vector2D): Vector2D {
return new Vector2D(vector1.x + vector2.x, vector1.y + vector2.y);
}
/**
* The subtract function takes two Vector2D objects as parameters and returns a new Vector2D object with the x and y
* components subtracted.
* @param {Vector2D} vector1 - The parameter `vector1` is an instance of the `Vector2D` class, representing a
* 2-dimensional vector. It has properties `x` and `y` which represent the x and y components of the vector
* respectively.
* @param {Vector2D} vector2 - The parameter "vector2" is a Vector2D object. It represents the second vector that you
* want to subtract from the first vector.
* @returns The method is returning a new Vector2D object with the x and y components subtracted from vector1 and
* vector2.
*/
static subtract(vector1: Vector2D, vector2: Vector2D): Vector2D {
return new Vector2D(vector1.x - vector2.x, vector1.y - vector2.y);
}
/**
* The function subtracts a given value from the x and y components of a Vector2D object and returns a new Vector2D
* object.
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector with
* x and y components.
* @param {number} value - The "value" parameter is a number that will be subtracted from both the x and y components
* of the "vector" parameter.
* @returns A new Vector2D object with the x and y values subtracted by the given value.
*/
static subtractValue(vector: Vector2D, value: number): Vector2D {
return new Vector2D(vector.x - value, vector.y - value);
}
/**
* The function multiplies a Vector2D object by a given value.
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector with
* x and y components.
* @param {number} value - The "value" parameter is a number that represents the value by which the x and y components
* of the vector will be multiplied.
* @returns A new Vector2D object with the x and y values multiplied by the given value.
*/
static multiply(vector: Vector2D, value: number): Vector2D {
return new Vector2D(vector.x * value, vector.y * value);
}
/**
* The function divides the x and y components of a Vector2D by a given value and returns a new Vector2D.
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector with
* x and y components.
* @param {number} value - The value parameter is a number that will be used to divide the x and y components of the
* vector.
* @returns A new instance of the Vector2D class with the x and y values divided by the given value.
*/
static divide(vector: Vector2D, value: number): Vector2D {
return new Vector2D(vector.x / value, vector.y / value);
}
/**
* The function checks if two Vector2D objects are equal by comparing their x and y values.
* @param {Vector2D} vector1 - The parameter `vector1` is of type `Vector2D`, which represents a 2-dimensional vector.
* It has two properties: `x` and `y`, which represent the x and y components of the vector, respectively.
* @param {Vector2D} vector2 - The parameter "vector2" is of type Vector2D.
* @returns a boolean value, which indicates whether the two input vectors are equal or not.
*/
static equals(vector1: Vector2D, vector2: Vector2D): boolean {
return vector1.x === vector2.x && vector1.y === vector2.y;
}
/**
* The function checks if two Vector2D objects are equal within a specified rounding factor.
* @param {Vector2D} vector1 - The first vector to compare.
* @param {Vector2D} vector2 - The parameter "vector2" is a Vector2D object, which represents a 2-dimensional vector.
* It is used as one of the inputs for the "equalsRounded" function.
* @param [roundingFactor=12] - The roundingFactor parameter is used to determine the threshold for considering two
* vectors as equal. If the absolute difference in the x and y components of the vectors is less than the
* roundingFactor, the vectors are considered equal.
* @returns a boolean value.
*/
static equalsRounded(vector1: Vector2D, vector2: Vector2D, roundingFactor = 12): boolean {
const vector = Vector2D.abs(Vector2D.subtract(vector1, vector2));
if (vector.x < roundingFactor && vector.y < roundingFactor) {
return true;
}
return false;
}
/**
* The normalize function takes a vector as input and returns a normalized version of the vector.Normalizes the vector if it matches a certain condition
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D.
* @returns the normalized vector if its length is greater than a very small value (epsilon), otherwise it returns the
* original vector.
*/
static normalize(vector: Vector2D): Vector2D {
const length = vector.length;
if (length > 2.220446049250313e-16) {
// Epsilon
return Vector2D.divide(vector, length);
}
return vector;
}
/**
* The function truncates a vector to a maximum length if it exceeds that length.Adjusts x and y so that the length of the vector does not exceed max
* @param {Vector2D} vector - A 2D vector represented by the Vector2D class.
* @param {number} max - The `max` parameter is a number that represents the maximum length that the `vector` should
* have.
* @returns either the original vector or a truncated version of the vector, depending on whether the length of the
* vector is greater than the maximum value specified.
*/
static truncate(vector: Vector2D, max: number): Vector2D {
if (vector.length > max) {
return Vector2D.multiply(Vector2D.normalize(vector), max);
}
return vector;
}
/**
* The function returns a new Vector2D object that is perpendicular to the input vector.The vector that is perpendicular to this one
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D.
* @returns A new Vector2D object is being returned.
*/
static perp(vector: Vector2D): Vector2D {
return new Vector2D(-vector.y, vector.x);
}
/**
* The reverse function takes a Vector2D object and returns a new Vector2D object with the negated x and y values.
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector. It
* has two properties: "x" and "y", which represent the x and y components of the vector, respectively.
* @returns A new Vector2D object with the negated x and y values of the input vector. Returns the vector that is the reverse of this vector
*/
static reverse(vector: Vector2D): Vector2D {
return new Vector2D(-vector.x, -vector.y);
}
/**
* The function takes a Vector2D object as input and returns a new Vector2D object with the absolute values of its x
* and y components.
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector. It
* has two properties: "x" and "y", which represent the x and y components of the vector, respectively.
* @returns The method is returning a new Vector2D object with the absolute values of the x and y components of the
* input vector.
*/
static abs(vector: Vector2D): Vector2D {
return new Vector2D(Math.abs(vector.x), Math.abs(vector.y));
}
/**
* The dot function calculates the dot product of two 2D vectors.The dot product of v1 and v2
* @param {Vector2D} vector1 - The parameter `vector1` represents a 2D vector with its x and y components.
* @param {Vector2D} vector2 - The "vector2" parameter is a Vector2D object. It represents a two-dimensional vector
* with an x and y component.
* @returns The dot product of the two input vectors.
*/
static dot(vector1: Vector2D, vector2: Vector2D): number {
return vector1.x * vector2.x + vector1.y * vector2.y;
}
// /**
// * Transform vectors based on the current tranformation matrices: translation, rotation and scale
// * @param vectors The vectors to transform
// */
// static transform(vector: Vector2D, transformation: Matrix2D): Vector2D {
// return Matrix2D.multiplyByVector(transformation, vector)
// }
// /**
// * Transform vectors based on the current tranformation matrices: translation, rotation and scale
// * @param vectors The vectors to transform
// */
// static transformList(vectors: Vector2D[], transformation: Matrix2D): Vector2D[] {
// return vectors.map(vector => Matrix2D.multiplyByVector(transformation, vector))
// }
/**
* The function calculates the distance between two points in a two-dimensional space.
* @param {Vector2D} vector1 - The parameter `vector1` represents the first vector in 2D space, while `vector2`
* represents the second vector. Each vector has an `x` and `y` component, which represent their respective coordinates
* in the 2D space.
* @param {Vector2D} vector2 - The `vector2` parameter represents the second vector in the calculation of distance. It
* is an instance of the `Vector2D` class, which typically has properties `x` and `y` representing the coordinates of
* the vector in a 2D space.
* @returns The distance between vector1 and vector2.
*/
static distance(vector1: Vector2D, vector2: Vector2D): number {
const ySeparation = vector2.y - vector1.y;
const xSeparation = vector2.x - vector1.x;
return Math.sqrt(ySeparation * ySeparation + xSeparation * xSeparation);
}
/**
* The function calculates the squared distance between two 2D vectors.
* @param {Vector2D} vector1 - The parameter `vector1` represents the first vector, which is an instance of the
* `Vector2D` class. It contains the x and y coordinates of the vector.
* @param {Vector2D} vector2 - The `vector2` parameter represents the second vector in a two-dimensional space. It has
* properties `x` and `y` which represent the coordinates of the vector.
* @returns the square of the distance between the two input vectors.
*/
static distanceSq(vector1: Vector2D, vector2: Vector2D): number {
const ySeparation = vector2.y - vector1.y;
const xSeparation = vector2.x - vector1.x;
return ySeparation * ySeparation + xSeparation * xSeparation;
}
/**
* The sign function determines the sign of the cross product between two 2D vectors.
* (assuming the Y axis is pointing down, X axis to right like a Window app)
* @param {Vector2D} vector1 - The parameter `vector1` is of type `Vector2D`, which represents a 2-dimensional vector.
* It likely has properties `x` and `y` representing the x and y components of the vector, respectively.
* @param {Vector2D} vector2 - The above code defines a function called "sign" that takes two parameters: vector1 and
* vector2. Both vector1 and vector2 are of type Vector2D.
* @returns either -1 or 1. Returns positive if v2 is clockwise of this vector, negative if counterclockwise
*/
static sign(vector1: Vector2D, vector2: Vector2D): number {
if (vector1.y * vector2.x > vector1.x * vector2.y) {
return -1;
}
return 1;
}
/**
* The function calculates the angle between a given vector and the negative y-axis.
* @param {Vector2D} vector - The "vector" parameter is an instance of the Vector2D class, which represents a
* 2-dimensional vector. It has two properties: "x" and "y", which represent the x and y components of the vector,
* respectively.
* @returns the angle between the given vector and the vector (0, -1) in radians.Returns the angle between origin and the given vector in radians
*/
static angle(vector: Vector2D): number {
const origin = new Vector2D(0, -1);
const radian = Math.acos(Vector2D.dot(vector, origin) / (vector.length * origin.length));
return Vector2D.sign(vector, origin) === 1 ? Math.PI * 2 - radian : radian;
}
/**
* The function "random" generates a random Vector2D object with x and y values within the specified range.
* @param {number} maxX - The maxX parameter represents the maximum value for the x-coordinate of the random vector.
* @param {number} maxY - The `maxY` parameter represents the maximum value for the y-coordinate of the generated
* random vector.
* @returns a new instance of the Vector2D class with random x and y values.
*/
static random(maxX: number, maxY: number): Vector2D {
const randX = Math.floor(Math.random() * maxX - maxX / 2);
const randY = Math.floor(Math.random() * maxY - maxY / 2);
return new Vector2D(randX, randY);
}
/**
* The function sets the values of x and y to zero.
*/
zero(): void {
this.x = 0;
this.y = 0;
}
}

View file

@ -100,3 +100,8 @@ export const isWeakKey = (input: unknown): input is object => {
export const calcMinUnitsRequired = (totalQuantity: number, unitSize: number) =>
Math.floor((totalQuantity + unitSize - 1) / unitSize);
export const roundFixed = (num: number, digit: number = 10) => {
const multiplier = Math.pow(10, digit);
return Math.round(num * multiplier) / multiplier;
};

View file

@ -7,7 +7,7 @@ describe('Individual package BST operations test', () => {
bst.add([11, 11]);
bst.add([3, 3]);
const idsOrValues = [15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
bst.addMany(idsOrValues, false);
bst.addMany(idsOrValues, undefined);
expect(bst.root).toBeInstanceOf(BSTNode);
if (bst.root) expect(bst.root.key).toBe(11);
@ -204,7 +204,7 @@ describe('Individual package BST operations test', () => {
[5, { key: 5, keyA: 5 }]
];
objBST.addMany(values, false);
objBST.addMany(values, undefined);
expect(objBST.root).toBeInstanceOf(BSTNode);

View file

@ -1,54 +1,347 @@
import { MatrixNTI2D } from '../../../../src';
import { Matrix } from '../../../../src';
describe('MatrixNTI2D', () => {
it('should initialize a matrix with rows and columns', () => {
const numRows = 3;
const numCols = 4;
const matrix = new MatrixNTI2D({ row: numRows, col: numCols });
describe('Matrix', () => {
let matrix: Matrix;
expect(matrix.toArray().length).toBe(numRows);
expect(matrix.toArray()[0].length).toBe(numCols);
beforeEach(() => {
// Initialize a 3x3 matrix with zeros
matrix = new Matrix([
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]);
});
it('should initialize all elements with the provided initial value', () => {
const numRows = 3;
const numCols = 4;
const initialValue = 42;
const matrix = new MatrixNTI2D({ row: numRows, col: numCols, initialVal: initialValue });
const matrixArray = matrix.toArray();
for (let i = 0; i < numRows; i++) {
for (let j = 0; j < numCols; j++) {
expect(matrixArray[i][j]).toBe(initialValue);
}
}
it('should create a matrix with the correct dimensions and initial values', () => {
expect(matrix.rows).toBe(3);
expect(matrix.cols).toBe(3);
expect(matrix.data).toEqual([
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]);
});
it('should initialize all elements with 0 if no initial value is provided', () => {
const numRows = 3;
const numCols = 4;
const matrix = new MatrixNTI2D({ row: numRows, col: numCols });
const matrixArray = matrix.toArray();
for (let i = 0; i < numRows; i++) {
for (let j = 0; j < numCols; j++) {
expect(matrixArray[i][j]).toBe(0);
}
}
it('should get a value at a specific position', () => {
expect(matrix.get(1, 1)).toBe(0);
expect(matrix.get(2, 2)).toBe(0);
});
it('should convert the matrix to a two-dimensional array', () => {
const numRows = 2;
const numCols = 3;
const matrix = new MatrixNTI2D({ row: numRows, col: numCols, initialVal: 1 });
it('should set a value at a specific position', () => {
matrix.set(1, 1, 42);
expect(matrix.get(1, 1)).toBe(42);
});
const matrixArray = matrix.toArray();
expect(matrixArray.length).toBe(numRows);
for (let i = 0; i < numRows; i++) {
expect(matrixArray[i].length).toBe(numCols);
for (let j = 0; j < numCols; j++) {
expect(matrixArray[i][j]).toBe(1);
}
}
it('should not allow getting or setting values at invalid positions', () => {
expect(matrix.get(-1, 0)).toBeUndefined();
expect(matrix.get(0, 10)).toBeUndefined();
const originalValue = matrix.get(1, 1);
matrix.set(-1, 1, 42);
matrix.set(1, 10, 42);
expect(matrix.get(1, 1)).toBe(originalValue);
});
describe('add', () => {
it('should add two matrices correctly', () => {
const matrixA = new Matrix([
[1, 2],
[3, 4]
]);
const matrixB = new Matrix([
[5, 6],
[7, 8]
]);
const expectedResult = new Matrix([
[6, 8],
[10, 12]
]);
const result = matrixA.add(matrixB);
expect(result?.data).toEqual(expectedResult.data);
});
it('should throw an error for matrices with mismatched dimensions', () => {
const matrixA = new Matrix([
[1, 2],
[3, 4]
]);
const matrixB = new Matrix([
[5, 6, 7],
[8, 9, 10]
]);
expect(() => matrixA.add(matrixB)).toThrowError('Matrix dimensions must match for addition.');
});
it('should throw an error for matrices with mismatched dimensions', () => {
const matrixA = new Matrix([
[1, 2],
[3, 4]
]);
const matrixB = new Matrix([
[5, 6, 7],
[8, 9, 10]
]);
expect(() => matrixA.add(matrixB)).toThrowError('Matrix dimensions must match for addition.');
});
});
describe('subtract', () => {
it('should subtract two matrices with numbers correctly', () => {
const matrixA = new Matrix([
[5, 6],
[7, 8]
]);
const matrixB = new Matrix([
[1, 2],
[3, 4]
]);
const expectedResult = new Matrix([
[4, 4],
[4, 4]
]);
const result = matrixA.subtract(matrixB);
expect(result?.data).toEqual(expectedResult.data);
});
it('should subtract two matrices with custom subtract function correctly', () => {
const customSubtractFn = (a: number, b: number) => a * 10 - b; // Custom subtraction for arrays
const matrixA = new Matrix(
[
[5, 6],
[7, 8]
],
{ subtractFn: customSubtractFn }
);
const matrixB = new Matrix(
[
[1, 2],
[3, 4]
],
{ subtractFn: customSubtractFn }
);
const expectedResult = new Matrix(
[
[49, 58],
[67, 76]
],
{ subtractFn: customSubtractFn }
);
const result = matrixA.subtract(matrixB);
expect(result?.data).toEqual(expectedResult.data);
});
it('should throw an error for matrices with mismatched dimensions', () => {
const matrixA = new Matrix([
[1, 2],
[3, 4]
]);
const matrixB = new Matrix([
[5, 6, 7],
[8, 9, 10]
]);
expect(() => matrixA.subtract(matrixB)).toThrowError('Matrix dimensions must match for subtraction.');
});
});
describe('multiply', () => {
it('should multiply two matrices with numbers correctly', () => {
const matrixA = new Matrix([
[1, 2],
[3, 4]
]);
const matrixB = new Matrix([
[5, 6],
[7, 8]
]);
const expectedResult = new Matrix([
[19, 22],
[43, 50]
]);
const result = matrixA.multiply(matrixB);
expect(result?.data).toEqual(expectedResult.data);
});
it('should multiply two matrices with custom multiply function correctly', () => {
const customMultiplyFn = (a: number, b: number) => a * 10 * b; // Custom multiplication for arrays
const matrixA = new Matrix(
[
[1, 2],
[3, 4]
],
{ multiplyFn: customMultiplyFn }
);
const matrixB = new Matrix(
[
[5, 6],
[7, 8]
],
{ multiplyFn: customMultiplyFn }
);
const result = matrixA.multiply(matrixB);
expect(result?.data).toEqual([
[190, 220],
[430, 500]
]);
});
it('should throw an error for matrices with mismatched dimensions', () => {
const matrixA = new Matrix([
[1, 2, 3],
[3, 4, 5]
]);
const matrixB = new Matrix([
[5, 6, 7],
[8, 9, 1]
]);
expect(() => matrixA.multiply(matrixB)).toThrowError(
'Matrix dimensions must be compatible for multiplication (A.cols = B.rows).'
);
});
});
describe('transpose', () => {
test('should transpose a matrix with numeric values correctly', () => {
const originalMatrix = new Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const transposedMatrix = originalMatrix.transpose();
expect(transposedMatrix.rows).toBe(originalMatrix.cols);
expect(transposedMatrix.cols).toBe(originalMatrix.rows);
expect(transposedMatrix.data).toEqual([
[1, 4, 7],
[2, 5, 8],
[3, 6, 9]
]);
});
test('should transpose an empty matrix correctly', () => {
const originalMatrix = new Matrix([]);
const transposedMatrix = originalMatrix.transpose();
expect(transposedMatrix.rows).toBe(0);
expect(transposedMatrix.cols).toBe(0);
expect(transposedMatrix.data).toEqual([]);
});
test('should throw an error when transposing a non-rectangular matrix', () => {
const originalMatrix = new Matrix([
[1, 2, 3],
[4, 5]
]);
// Using a lambda to call transpose because Jest expects the error to be thrown within a function
expect(() => originalMatrix.transpose()).toThrowError('Matrix must be rectangular for transposition.');
});
});
describe('inverse', () => {
it('should calculate the inverse of a 2x2 matrix', () => {
const data: number[][] = [
[1, 2],
[3, 4]
];
const matrix = new Matrix(data);
const inverseMatrix = matrix.inverse();
const expectedInverse: number[][] = [
[-2, 1],
[1.5, -0.5]
];
expect(inverseMatrix?.data).toEqual(expectedInverse);
});
it('should calculate the inverse of a 3x3 matrix', () => {
const data: number[][] = [
[4, 7, 2],
[2, 6, 3],
[1, 2, 5]
];
const matrix = new Matrix(data);
const inverseMatrix = matrix.inverse();
// const expectedInverse: number[][] = [
// [24 / 43, -31 / 43, 9 / 43],
// [-7 / 43, 18 / 43, -8 / 43],
// [-2 / 43, -1 / 43, 10 / 43],
// ];
expect(inverseMatrix?.data).toEqual([
[0.558139534883721, -0.7209302325581396, 0.2093023255813954],
[-0.16279069767441862, 0.4186046511627907, -0.18604651162790697],
[-0.046511627906976744, -0.023255813953488372, 0.23255813953488372]
]);
});
});
describe('dot', () => {
test('should calculate the dot product of two matrices', () => {
const matrix1 = new Matrix([
[1, 2],
[3, 4]
]);
const matrix2 = new Matrix([
[5, 6],
[7, 8]
]);
const resultMatrix = matrix1.dot(matrix2);
expect(resultMatrix?.data).toEqual([
[19, 22],
[43, 50]
]);
});
test('should throw an error for incompatible matrices', () => {
const matrix1 = new Matrix([
[1, 2],
[3, 4]
]);
const matrix2 = new Matrix([
[5, 6, 7],
[8, 9, 10],
[18, 19, 110]
]);
expect(() => matrix1.dot(matrix2)).toThrowError(
'Number of columns in the first matrix must be equal to the number of rows in the second matrix for dot product.'
);
});
it('should throw an error for incompatible matrices', () => {
const matrixA = new Matrix([
[1, 2],
[3, 4]
]);
const matrixB = new Matrix([
[5, 6],
[7, 8],
[9, 10]
]);
expect(() => matrixA.dot(matrixB)).toThrowError(
'Number of columns in the first matrix must be equal to the number of rows in the second matrix for dot product.'
);
});
});
});

View file

@ -1,345 +0,0 @@
import { Matrix2D, Vector2D } from '../../../../src';
import { isDebugTest } from '../../../config';
const isDebug = isDebugTest;
describe('Matrix2D', () => {
it('should initialize with default identity matrix', () => {
const matrix = new Matrix2D();
const expectedMatrix = Matrix2D.identity;
expect(matrix.m).toEqual(expectedMatrix);
});
it('should initialize with provided 2D array', () => {
const inputMatrix = [
[2, 0, 0],
[0, 3, 0],
[0, 0, 1]
];
const matrix = new Matrix2D(inputMatrix);
expect(matrix.m).toEqual(inputMatrix);
});
it('should initialize with provided Vector2D', () => {
expect(true).toBeTruthy();
});
it('should add two matrices correctly', () => {
const matrix1 = new Matrix2D([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const matrix2 = new Matrix2D([
[9, 8, 7],
[6, 5, 4],
[3, 2, 1]
]);
const expectedMatrix = [
[10, 10, 10],
[10, 10, 10],
[10, 10, 10]
];
const result = Matrix2D.add(matrix1, matrix2);
expect(result.m).toEqual(expectedMatrix);
});
it('should subtract two matrices correctly', () => {
const matrix1 = new Matrix2D([
[9, 8, 7],
[6, 5, 4],
[3, 2, 1]
]);
const matrix2 = new Matrix2D([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const expectedMatrix = [
[8, 6, 4],
[2, 0, -2],
[-4, -6, -8]
];
const result = Matrix2D.subtract(matrix1, matrix2);
expect(result.m).toEqual(expectedMatrix);
});
it('should multiply two matrices correctly', () => {
const matrix1 = new Matrix2D([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const matrix2 = new Matrix2D([
[9, 8, 7],
[6, 5, 4],
[3, 2, 1]
]);
const expectedMatrix = [
[30, 24, 18],
[84, 69, 54],
[138, 114, 90]
];
const result = Matrix2D.multiply(matrix1, matrix2);
expect(result.m).toEqual(expectedMatrix);
});
it('should multiply a matrix by a Vector2D correctly', () => {
expect(true).toBeTruthy();
});
it('should scale a matrix by a value correctly', () => {
expect(true).toBeTruthy();
});
it('should rotate a matrix by radians correctly', () => {
expect(true).toBeTruthy();
});
it('should translate a matrix by a Vector2D correctly', () => {
const translationVector = new Vector2D(2, 3);
const expectedMatrix = [
[1, 0, 2],
[0, 1, 3],
[0, 0, 1]
];
const result = Matrix2D.translate(translationVector);
expect(result.m).toEqual(expectedMatrix);
});
it('should create a view matrix correctly', () => {
expect(true).toBeTruthy();
});
it('should multiply a matrix by a value correctly', () => {
const matrix = new Matrix2D([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const value = 2;
const expectedMatrix = [
[2, 4, 6],
[8, 10, 12],
[14, 16, 18]
];
const result = Matrix2D.multiplyByValue(matrix, value);
expect(result.m).toEqual(expectedMatrix);
});
});
describe('Vector2D', () => {
it('should create a vector with default values', () => {
const vector = new Vector2D();
expect(vector.x).toBe(0);
expect(vector.y).toBe(0);
expect(vector.w).toBe(1);
});
it('should correctly calculate vector length', () => {
const vector = new Vector2D(3, 4);
expect(vector.length).toBe(5);
});
it('should correctly add two vectors', () => {
const vector1 = new Vector2D(2, 3);
const vector2 = new Vector2D(1, 2);
const result = Vector2D.add(vector1, vector2);
expect(result.x).toBe(3);
expect(result.y).toBe(5);
});
// Add more test cases for Vector2D methods
});
describe('Matrix2D', () => {
it('should create an identity matrix by default', () => {
const matrix = new Matrix2D();
expect(matrix.m).toEqual(Matrix2D.identity);
});
it('should correctly add two matrices', () => {
const matrix1 = new Matrix2D([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const matrix2 = new Matrix2D([
[9, 8, 7],
[6, 5, 4],
[3, 2, 1]
]);
const result = Matrix2D.add(matrix1, matrix2);
expect(result.m).toEqual([
[10, 10, 10],
[10, 10, 10],
[10, 10, 10]
]);
});
// Add more test cases for Matrix2D methods
});
describe('Matrix2D', () => {
it('should create a matrix with default identity values', () => {
const matrix = new Matrix2D();
expect(matrix.m).toEqual(Matrix2D.identity);
});
it('should create a matrix from a Vector2D', () => {
const vector = new Vector2D(2, 3);
const matrix = new Matrix2D(vector);
expect(matrix.m).toEqual([
[2, 0, 0],
[3, 1, 0],
[1, 0, 1]
]);
});
it('should correctly add two matrices', () => {
const matrix1 = new Matrix2D([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const matrix2 = new Matrix2D([
[9, 8, 7],
[6, 5, 4],
[3, 2, 1]
]);
const result = Matrix2D.add(matrix1, matrix2);
expect(result.m).toEqual([
[10, 10, 10],
[10, 10, 10],
[10, 10, 10]
]);
});
it('should correctly subtract two matrices', () => {
const matrix1 = new Matrix2D([
[5, 4, 3],
[2, 1, 0],
[0, 1, 2]
]);
const matrix2 = new Matrix2D([
[4, 3, 2],
[1, 0, 1],
[2, 1, 0]
]);
const result = Matrix2D.subtract(matrix1, matrix2);
expect(result.m).toEqual([
[1, 1, 1],
[1, 1, -1],
[-2, 0, 2]
]);
});
it('should correctly multiply two matrices', () => {
const matrix1 = new Matrix2D([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const matrix2 = new Matrix2D([
[9, 8, 7],
[6, 5, 4],
[3, 2, 1]
]);
const result = Matrix2D.multiply(matrix1, matrix2);
expect(result.m).toEqual([
[30, 24, 18],
[84, 69, 54],
[138, 114, 90]
]);
});
it('should correctly multiply a matrix by a value', () => {
const matrix = new Matrix2D([
[2, 3, 4],
[5, 6, 7],
[8, 9, 10]
]);
const value = 2;
const result = Matrix2D.multiplyByValue(matrix, value);
expect(result.m).toEqual([
[4, 6, 8],
[10, 12, 14],
[16, 18, 20]
]);
});
it('should correctly multiply a matrix by a vector', () => {
const matrix = new Matrix2D([
[2, 3, 4],
[5, 6, 7],
[8, 9, 10]
]);
const vector = new Vector2D(2, 3);
const result = Matrix2D.multiplyByVector(matrix, vector);
isDebug && console.log(JSON.stringify(result));
expect(result).toEqual({ x: 17, y: 35, w: 1 });
});
it('should correctly create a view matrix', () => {
const width = 800;
const height = 600;
const viewMatrix = Matrix2D.view(width, height);
expect(viewMatrix.m).toEqual([
[1, 0, 400],
[0, -1, 300],
[0, 0, 1]
]);
});
it('should correctly scale a matrix', () => {
const factor = 3;
const scaledMatrix = Matrix2D.scale(factor);
expect(scaledMatrix.m).toEqual([
[3, 0, 0],
[0, 3, 0],
[0, 0, 3]
]);
});
it('should correctly rotate a matrix', () => {
const radians = Math.PI / 4; // 45 degrees
const rotationMatrix = Matrix2D.rotate(radians);
isDebug && console.log(JSON.stringify(rotationMatrix.m));
expect(rotationMatrix.m).toEqual([
[0.7071067811865476, -0.7071067811865475, 0],
[0.7071067811865475, 0.7071067811865476, 0],
[0, 0, 1]
]);
});
it('should correctly translate a matrix', () => {
const translationVector = new Vector2D(2, 3);
const translationMatrix = Matrix2D.translate(translationVector);
expect(translationMatrix.m).toEqual([
[1, 0, 2],
[0, 1, 3],
[0, 0, 1]
]);
});
it('should correctly convert a matrix to a vector', () => {
const matrix = new Matrix2D([
[2, 3, 4],
[5, 6, 7],
[8, 9, 10]
]);
const vector = matrix.toVector();
expect(vector).toEqual(new Vector2D(2, 5));
});
});

View file

@ -1,171 +0,0 @@
import { Vector2D } from '../../../../src';
describe('Vector2D', () => {
it('should create a vector with default values', () => {
const vector = new Vector2D();
expect(vector.x).toBe(0);
expect(vector.y).toBe(0);
expect(vector.w).toBe(1);
});
it('should correctly check if a vector is zero', () => {
const nonZeroVector = new Vector2D(3, 4);
const zeroVector = new Vector2D(0, 0);
expect(nonZeroVector.isZero).toBe(false);
expect(zeroVector.isZero).toBe(true);
});
it('should correctly calculate vector length', () => {
const vector = new Vector2D(3, 4);
expect(vector.length).toBe(5);
});
it('should correctly calculate squared vector length', () => {
const vector = new Vector2D(3, 4);
expect(vector.lengthSq).toBe(25);
});
it('should correctly round vector components', () => {
const vector = new Vector2D(3.6, 4.3);
const roundedVector = vector.rounded;
expect(roundedVector.x).toBe(4);
expect(roundedVector.y).toBe(4);
});
it('should correctly add two vectors', () => {
const vector1 = new Vector2D(2, 3);
const vector2 = new Vector2D(1, 2);
const result = Vector2D.add(vector1, vector2);
expect(result.x).toBe(3);
expect(result.y).toBe(5);
});
it('should correctly subtract two vectors', () => {
const vector1 = new Vector2D(4, 5);
const vector2 = new Vector2D(1, 2);
const result = Vector2D.subtract(vector1, vector2);
expect(result.x).toBe(3);
expect(result.y).toBe(3);
});
it('should correctly subtract value from a vector', () => {
const vector = new Vector2D(5, 7);
const result = Vector2D.subtractValue(vector, 3);
expect(result.x).toBe(2);
expect(result.y).toBe(4);
});
it('should correctly multiply a vector by a value', () => {
const vector = new Vector2D(2, 3);
const result = Vector2D.multiply(vector, 4);
expect(result.x).toBe(8);
expect(result.y).toBe(12);
});
it('should correctly divide a vector by a value', () => {
const vector = new Vector2D(6, 8);
const result = Vector2D.divide(vector, 2);
expect(result.x).toBe(3);
expect(result.y).toBe(4);
});
it('should correctly check if two vectors are equal', () => {
const vector1 = new Vector2D(3, 4);
const vector2 = new Vector2D(3, 4);
const vector3 = new Vector2D(4, 5);
expect(Vector2D.equals(vector1, vector2)).toBe(true);
expect(Vector2D.equals(vector1, vector3)).toBe(false);
});
it('should correctly check if two vectors are equal within a rounding factor', () => {
const vector1 = new Vector2D(3, 4);
const vector2 = new Vector2D(2.9, 3.9);
const vector3 = new Vector2D(4, 5);
expect(Vector2D.equalsRounded(vector1, vector2, 0.2)).toBe(true);
expect(Vector2D.equalsRounded(vector1, vector3, 0.2)).toBe(false);
});
it('should correctly normalize a vector', () => {
const vector = new Vector2D(3, 4);
const normalized = Vector2D.normalize(vector);
expect(normalized.x).toBeCloseTo(0.6);
expect(normalized.y).toBeCloseTo(0.8);
});
it('should correctly truncate a vector', () => {
const vector = new Vector2D(3, 4);
const truncated = Vector2D.truncate(vector, 4);
expect(truncated.length).toBeLessThanOrEqual(4);
});
it('should correctly get the perpendicular vector', () => {
const vector = new Vector2D(3, 4);
const perpendicular = Vector2D.perp(vector);
expect(Vector2D.dot(vector, perpendicular)).toBe(0);
});
it('should correctly reverse the vector', () => {
const vector = new Vector2D(3, 4);
const reversed = Vector2D.reverse(vector);
expect(reversed.x).toBe(-3);
expect(reversed.y).toBe(-4);
});
it('should correctly get the absolute vector', () => {
const vector = new Vector2D(-3, 4);
const absVector = Vector2D.abs(vector);
expect(absVector.x).toBe(3);
expect(absVector.y).toBe(4);
});
it('should correctly calculate the dot product of two vectors', () => {
const vector1 = new Vector2D(2, 3);
const vector2 = new Vector2D(4, 5);
const dotProduct = Vector2D.dot(vector1, vector2);
expect(dotProduct).toBe(23);
});
it('should correctly calculate the distance between two vectors', () => {
const vector1 = new Vector2D(1, 1);
const vector2 = new Vector2D(4, 5);
const distance = Vector2D.distance(vector1, vector2);
expect(distance).toBeCloseTo(5);
});
it('should correctly calculate the squared distance between two vectors', () => {
const vector1 = new Vector2D(1, 1);
const vector2 = new Vector2D(4, 5);
const distanceSq = Vector2D.distanceSq(vector1, vector2);
expect(distanceSq).toBe(25);
});
it('should correctly determine the sign of the cross product of two vectors', () => {
const vector1 = new Vector2D(2, 3);
const vector2 = new Vector2D(4, 5);
const sign = Vector2D.sign(vector1, vector2);
expect(sign).toBe(-1); // Assuming specific vector values, the result may vary
});
it('should correctly calculate the angle between a vector and the negative y-axis', () => {
const vector = new Vector2D(3, 4);
const angle = Vector2D.angle(vector);
expect(angle).toBeCloseTo(2.498091544796509, 3);
});
it('should create a random vector within the specified range', () => {
const maxX = 10;
const maxY = 10;
const randomVector = Vector2D.random(maxX, maxY);
expect(randomVector.x).toBeGreaterThanOrEqual(-maxX / 2);
expect(randomVector.x).toBeLessThanOrEqual(maxX / 2);
expect(randomVector.y).toBeGreaterThanOrEqual(-maxY / 2);
expect(randomVector.y).toBeLessThanOrEqual(maxY / 2);
});
it('should zero the vector components', () => {
const vector = new Vector2D(3, 4);
vector.zero();
expect(vector.x).toBe(0);
expect(vector.y).toBe(0);
});
});

30
typedoc.json Normal file
View file

@ -0,0 +1,30 @@
{
"visibilityFilters": {
"protected": false,
"private": false,
"inherited": true,
"external": false,
"@alpha": false,
"@beta": false
},
"excludeNotDocumented": true,
"excludeNotDocumentedKinds": [
"Module",
"Namespace",
"Enum",
"Variable",
"Function",
"Interface",
"Constructor",
"Property",
"Method",
"CallSignature",
"IndexSignature",
"ConstructorSignature",
"Accessor",
"GetSignature",
"SetSignature",
"TypeAlias",
"Reference"
]
}