UNPKG

munkres

Version:

A lightweight and efficient implementation of the Munkres (Hungarian) algorithm for optimal assignment in square and rectangular matrices.

1 lines 124 kB
{"version":3,"file":"munkres.cjs","sources":["../src/utils/matrixLike.ts","../src/utils/matrix.ts","../src/helpers.ts","../src/utils/is.ts","../src/utils/arrayLike.ts","../src/utils/mutableArrayLike.ts","../src/core/bigMunkresB.ts","../src/core/bigMunkres.ts","../src/core/numMunkresB.ts","../src/core/numMunkres.ts","../src/core/munkres.ts","../src/utils/matching.ts","../src/munkres.ts"],"sourcesContent":["import type { MatrixLike } from \"../types/matrixLike.ts\";\n\n/**\n * Finds the maximum value in a given matrix.\n *\n * @param matrix - The matrix.\n *\n * @returns The maximum value, or `undefined` if the matrix is empty.\n *\n * @example\n * const matrix = [\n * [1, 3, 2],\n * [4, 0, 6],\n * [7, 5, 8]\n * ];\n * console.log(getMax(matrix)); // Output: 8\n *\n * @example\n * const matrix = [\n * [1n, 3n, 2n],\n * [4n, 0n, 6n],\n * [7n, 5n, 8n]\n * ];\n * console.log(getMax(matrix)); // Output: 8n\n *\n * @example\n * const matrix = [\n * ['b', 'd', 'c'],\n * ['e', 'a', 'g'],\n * ['h', 'f', 'i']\n * ];\n * console.log(getMax(matrix)); // Output: 'i'\n */\nexport function getMax(matrix: MatrixLike<number>): number | undefined;\nexport function getMax(matrix: MatrixLike<bigint>): bigint | undefined;\nexport function getMax(matrix: MatrixLike<string>): string | undefined;\nexport function getMax<T extends number | bigint | string>(\n matrix: MatrixLike<T>,\n): T | undefined {\n const Y = matrix.length;\n const X = matrix[0]?.length ?? 0;\n if (Y <= 0 || X <= 0) {\n return undefined;\n }\n\n let max = matrix[0][0];\n for (let y = 0; y < Y; ++y) {\n const row = matrix[y];\n for (let x = 0; x < X; ++x) {\n if (max < row[x]) {\n max = row[x];\n }\n }\n }\n\n return max;\n}\n\n/**\n * Finds the minimum value in a given matrix.\n *\n * @param matrix - The matrix.\n *\n * @returns The minimum value, or `undefined` if the matrix is empty.\n *\n * @example\n * const matrix = [\n * [1, 3, 2],\n * [4, 0, 6],\n * [7, 5, 8]\n * ];\n * console.log(getMin(matrix)); // Output: 0\n *\n * @example\n * const matrix = [\n * [1n, 3n, 2n],\n * [4n, 0n, 6n],\n * [7n, 5n, 8n]\n * ];\n * console.log(getMin(matrix)); // Output: 0n\n *\n * @example\n * const matrix = [\n * ['b', 'd', 'c'],\n * ['e', 'a', 'g'],\n * ['h', 'f', 'i']\n * ];\n * console.log(getMin(matrix)); // Output: 'a'\n */\nexport function getMin(matrix: MatrixLike<number>): number | undefined;\nexport function getMin(matrix: MatrixLike<bigint>): bigint | undefined;\nexport function getMin(matrix: MatrixLike<string>): string | undefined;\nexport function getMin<T extends number | bigint | string>(\n matrix: MatrixLike<T>,\n): T | undefined {\n const Y = matrix.length;\n const X = matrix[0]?.length ?? 0;\n if (Y <= 0 || X <= 0) {\n return undefined;\n }\n\n let min = matrix[0][0];\n for (let y = 0; y < Y; ++y) {\n const row = matrix[y];\n for (let x = 0; x < X; ++x) {\n if (min > row[x]) {\n min = row[x];\n }\n }\n }\n\n return min;\n}\n","import type { Matrix } from \"../types/matrix.ts\";\nimport type { MatrixLike } from \"../types/matrixLike.ts\";\n\nimport { getMax } from \"./matrixLike.ts\";\n\n/**\n * Creates a matrix with specified rows and columns.\n *\n * The callback function is called for every combination of elements from the\n * `rows` and `columns` arrays, receiving the current row and column elements\n * as arguments, and its return value is used to populate the matrix.\n *\n * @param rows - An array of row elements.\n * @param columns - An array of column elements.\n * @param callbackFn - A function that produces values for the new matrix,\n * taking a row element and a column element as arguments.\n *\n * @returns A matrix populated by the results of the `callbackFn` function.\n *\n * @example\n * const rows = [1, 2];\n * const cols = ['a', 'b', 'c'];\n * const callbackFn = (row, col) =\\> `${row}${col}`;\n *\n * const matrix = create(rows, cols, callbackFn);\n * // matrix is:\n * // [\n * // ['1a', '1b', '1c'],\n * // ['2a', '2b', '2c']\n * // ]\n */\nexport function create<R, C, T>(\n rows: ArrayLike<R>,\n columns: ArrayLike<C>,\n callbackFn: (row: R, col: C) => T,\n): Matrix<T> {\n const Y = rows.length;\n const X = columns.length;\n const mat = new Array<T[]>(Y);\n for (let y = 0; y < Y; ++y) {\n const row = new Array<T>(X);\n for (let x = 0; x < X; ++x) {\n row[x] = callbackFn(rows[y], columns[x]);\n }\n mat[y] = row;\n }\n return mat;\n}\n\n/**\n * Flips a matrix horizontally.\n *\n * After the flip, the element at position `[y][x]` moves to `[y][M-x-1]`,\n * where `M` is the number of columns in the matrix.\n *\n * @param matrix - The matrix to be flipped. Modified in place.\n *\n * @example\n * const matrix = [\n * [1, 2, 3],\n * [4, 5, 6],\n * [7, 8, 9]\n * ];\n *\n * flipH(matrix);\n * // matrix is now:\n * // [\n * // [3, 2, 1],\n * // [6, 5, 4],\n * // [9, 8, 7]\n * // ]\n */\nexport function flipH<T>(matrix: Matrix<T>): void {\n const Y = matrix.length;\n for (let y = 0; y < Y; ++y) {\n matrix[y].reverse();\n }\n}\n\n/**\n * Flips a matrix vertically.\n *\n * After the flip, the element at position `[y][x]` moves to `[N-y-1][x]`,\n * where `N` is the number of rows in the matrix.\n *\n * @param matrix - The matrix to be flipped. Modified in place.\n *\n * @example\n * const matrix = [\n * [1, 2, 3],\n * [4, 5, 6],\n * [7, 8, 9]\n * ];\n *\n * flipV(matrix);\n * // matrix is now:\n * // [\n * // [7, 8, 9],\n * // [4, 5, 6],\n * // [1, 2, 3]\n * // ]\n */\nexport function flipV<T>(matrix: Matrix<T>): void {\n matrix.reverse();\n}\n\n/**\n * Creates a {@link Matrix} from a given {@link MatrixLike}.\n *\n * @param matrix - The matrix to be copied.\n *\n * @returns A copy of the given matrix.\n */\nexport function from<T>(matrix: MatrixLike<T>): Matrix<T> {\n const Y = matrix.length;\n const dupe: Matrix<T> = new Array(Y);\n for (let y = 0; y < Y; ++y) {\n const rowA = matrix[y];\n const X = rowA.length;\n const rowB = new Array(X);\n for (let x = 0; x < X; ++x) {\n rowB[x] = rowA[x];\n }\n dupe[y] = rowB;\n }\n return dupe;\n}\n\n/**\n * Generates a matrix with specified rows and columns.\n *\n * The callback function is called with every combination of row and column indices,\n * and its return value is used to populate the matrix.\n *\n * @param rows - The number of rows.\n * @param columns - The number of columns.\n * @param callbackFn - A function that produces values for the new matrix,\n * taking a row and column index as arguments.\n *\n * @returns A matrix populated by the results of the `callbackFn` function.\n *\n * @example\n * const rows = 2;\n * const cols = 3;\n * const callbackFn = (row, col) =\\> `(${row},${col})`;\n *\n * const matrix = create(rows, cols, callbackFn);\n * // matrix is:\n * // [\n * // ['(0,0)', '(0,1)', '(0,2)'],\n * // ['(1,0)', '(1,1)', '(1,2)']\n * // ]\n */\nexport function gen<T>(\n rows: number,\n cols: number,\n callbackFn: (row: number, col: number) => T,\n): Matrix<T> {\n const matrix: Matrix<T> = new Array(rows);\n\n for (let r = 0; r < rows; ++r) {\n const row = new Array<T>(cols);\n for (let c = 0; c < cols; ++c) {\n row[c] = callbackFn(r, c);\n }\n matrix[r] = row;\n }\n\n return matrix;\n}\n\n/**\n * Inverts the values in a given matrix by\n * subtracting each element from a given large value.\n *\n * @param matrix - The matrix to be inverted. Modified in place.\n * @param bigVal - (Optional) A large value used as the basis for inversion.\n * If not provided, uses the maximum value in the matrix.\n *\n * @example\n * const matrix = [\n * [1, 2, 3],\n * [4, 5, 6]\n * ];\n *\n * invert(matrix);\n * // matrix is now:\n * // [\n * // [5, 4, 3],\n * // [2, 1, 0]\n * // ]\n *\n * @example\n * const matrix = [\n * [10, 20],\n * [30, 40]\n * ];\n *\n * invert(matrix, 50);\n * // matrix is now:\n * // [\n * // [40, 30],\n * // [20, 10]\n * // ]\n */\nexport function invert(matrix: Matrix<number>, bigVal?: number): void;\nexport function invert(matrix: Matrix<bigint>, bigVal?: bigint): void;\nexport function invert<T extends number | bigint>(\n matrix: Matrix<T>,\n bigVal?: T,\n): void {\n const Y = matrix.length;\n const X = matrix[0]?.length ?? 0;\n if (Y <= 0 || X <= 0) {\n return undefined;\n }\n\n bigVal = bigVal ?? (getMax(matrix as Matrix<number>)! as T);\n for (let y = 0; y < Y; ++y) {\n const row = matrix[y];\n for (let x = 0; x < X; ++x) {\n row[x] = (bigVal - row[x]) as T;\n }\n }\n}\n\n/**\n * Calls a defined callback function on each element\n * of a matrix, and returns a new matrix of the results.\n *\n * @param matrix - The original matrix.\n * @param callbackfn - A function that accepts up to four arguments.\n * Will be called once per element in the matrix.\n *\n * @returns The result matrix.\n *\n * @example\n * const matrix = [\n * [1, 3, 2],\n * [4, 0, 6],\n * [7, 5, 8]\n * ];\n * console.log(map(matrix, v =\\> v * v));\n * // Output: [\n * // [ 1, 9, 4],\n * // [16, 0, 36],\n * // [49, 25, 64]\n * // ]\n */\nexport function map<T, R>(\n matrix: MatrixLike<T>,\n callbackFn: (value: T, y: number, x: number, mat: typeof matrix) => R,\n): Matrix<R> {\n const Y = matrix.length;\n const out: Matrix<R> = new Array(Y);\n for (let y = 0; y < Y; ++y) {\n const from = matrix[y];\n const X = from.length;\n const to = new Array(X);\n for (let x = 0; x < X; ++x) {\n to[x] = callbackFn(from[x], y, x, matrix);\n }\n out[y] = to;\n }\n return out;\n}\n\n/**\n * Negates the values in a given matrix.\n *\n * @param matrix - The matrix to be negated. Modified in place.\n *\n * @example\n * const matrix = [\n * [1, 2, 3],\n * [4, -5, 6],\n * [7, 8, 9]\n * ];\n *\n * negate(matrix);\n * // matrix is now:\n * // [\n * // [-1, -2, -3],\n * // [-4, 5, -6],\n * // [-7, -8, -9]\n * // ]\n */\nexport function negate(matrix: Matrix<number>): void;\nexport function negate(matrix: Matrix<bigint>): void;\nexport function negate(matrix: Matrix<number | bigint>): void;\nexport function negate<T extends number | bigint>(matrix: Matrix<T>): void {\n const Y = matrix.length;\n const X = matrix[0]?.length ?? 0;\n for (let y = 0; y < Y; ++y) {\n const row = matrix[y];\n for (let x = 0; x < X; ++x) {\n row[x] = -row[x] as T;\n }\n }\n}\n\n/**\n * Pads a matrix to a specified size with a given fill value.\n *\n * The padding is applied from the ends (right) of each row and\n * the ends (bottom) of each column. If a dimension is already\n * at or above the desired value, no change is made to it.\n *\n * @param matrix - The matrix to pad. Modified in place.\n * @param height - The desired number of rows in the matrix.\n * @param width - The desired number of columns in the matrix.\n * @param fillValue - The value used for padding.\n */\nexport function pad<T>(\n matrix: Matrix<T>,\n height: number,\n width: number,\n fillValue: T,\n): void {\n padHeight(matrix, height, fillValue);\n padWidth(matrix, width, fillValue);\n}\n\n/**\n * Pads the height (number of rows) of a matrix with a given fill value.\n *\n * Rows are added to the end (bottom) of the matrix until its height reaches\n * `height`, with each new row filled with `fillValue`. If the matrix is\n * already at or above `height`, no change is made.\n *\n * @param matrix - The matrix to pad. Modified in place.\n * @param height - The desired number of rows in the matrix.\n * @param fillValue - The value to use for filling new rows.\n */\nexport function padHeight<T>(\n matrix: Matrix<T>,\n height: number,\n fillValue: T,\n): void {\n const Y = matrix.length;\n if (Y >= height) {\n return;\n }\n\n matrix.length = height;\n const X = matrix[0]?.length ?? 0;\n for (let y = Y; y < height; ++y) {\n matrix[y] = new Array<T>(X).fill(fillValue);\n }\n}\n\n/**\n * Pads the width (number of columns) of a matrix with a given fill value.\n *\n * Columns are added to the right of the matrix until its width reaches\n * `width`, with each new column filled with `fillValue`. If the matrix is\n * already at or above `width`, no change is made.\n *\n * @param matrix - The matrix to pad. Modified in place.\n * @param width - The desired number of columns in the matrix.\n * @param fillValue - The value to use for filling new columns.\n */\nexport function padWidth<T>(\n matrix: Matrix<T>,\n width: number,\n fillValue: T,\n): void {\n const X = matrix[0]?.length ?? 0;\n if (X >= width) {\n return;\n }\n\n const Y = matrix.length;\n for (let y = 0; y < Y; ++y) {\n matrix[y].length = width;\n matrix[y].fill(fillValue, X, width);\n }\n}\n\n/**\n * Rotates a matrix by 90 degrees clockwise.\n *\n * @param matrix - The matrix to be rotated. Modified in place.\n *\n * @example\n * const matrix = [\n * [1, 2],\n * [3, 4]\n * ];\n *\n * rot90(matrix);\n * // matrix is now:\n * // [\n * // [3, 1],\n * // [4, 2]\n * // ]\n *\n * @example\n * const matrix = [\n * [1, 2, 3],\n * [4, 5, 6]\n * ];\n *\n * rot90(matrix);\n * // matrix is now:\n * // [\n * // [4, 1],\n * // [5, 2],\n * // [6, 3]\n * // ]\n */\nexport function rot90<T>(matrix: Matrix<T>): void {\n flipV(matrix);\n transpose(matrix);\n}\n\n/**\n * Rotates a matrix by 90 degrees counterclockwise.\n *\n * @param matrix - The matrix to be rotated. Modified in place.\n *\n * @example\n * const matrix = [\n * [1, 2],\n * [3, 4]\n * ];\n *\n * rot90(matrix);\n * // matrix is now:\n * // [\n * // [2, 4],\n * // [1, 3]\n * // ]\n *\n * @example\n * const matrix = [\n * [1, 2, 3],\n * [4, 5, 6]\n * ];\n *\n * rot90(matrix);\n * // matrix is now:\n * // [\n * // [3, 6],\n * // [2, 5],\n * // [1, 4]\n * // ]\n */\nexport function rotNeg90<T>(matrix: Matrix<T>): void {\n transpose(matrix);\n flipV(matrix);\n}\n/**\n * Generates a string representation of a matrix.\n *\n * @param matrix - The matrix.\n * @param callbackFn - (Optional) A callback function to convert each element\n * to a string. Defaults to using each elements `toString` method.\n *\n * @returns A string representation of the matrix.\n */\nexport function toString<T>(\n matrix: MatrixLike<T>,\n callbackFn: (\n value: T,\n row: number,\n col: number,\n mat: typeof matrix,\n ) => string = (v) => `${v}`,\n): string {\n const strs: Matrix<string> = map(matrix, callbackFn);\n const Y = strs.length;\n const X = strs[0]?.length ?? 0;\n\n // For each column\n for (let x = 0; x < X; ++x) {\n // Get width\n let width = 0;\n for (let y = 0; y < Y; ++y) {\n width = Math.max(width, strs[y][x].length);\n }\n\n // Adjust width\n for (let y = 0; y < Y; ++y) {\n strs[y][x] = strs[y][x].padStart(width, \" \");\n }\n }\n\n // Create output\n const buf: string[] = new Array(Y);\n for (let y = 0; y < Y; ++y) {\n buf[y] = `[${strs[y].join(\", \")}]`;\n }\n\n // Return output\n return buf.join(\",\\n\");\n}\n\n/**\n * Transpose a given matrix, switching its rows and columns.\n *\n * In the transposed matrix, the value originally at position [y][x]\n * moves to [x][y], effectively turning rows of the original matrix into\n * columns in the output matrix, and vice versa.\n *\n * @param matrix - The matrix to transpose. Modified in place.\n *\n * @example\n * // Transpose a 2x3 matrix to a 3x2 matrix\n * const original = [\n * [1, 2, 3],\n * [4, 5, 6]\n * ];\n *\n * transpose(original);\n * // transposed is now:\n * // [\n * // [1, 4],\n * // [2, 5],\n * // [3, 6]\n * // ]\n */\nexport function transpose<T>(matrix: Matrix<T>): void {\n const Y = matrix.length;\n const X = matrix[0]?.length ?? 0;\n\n // Transpose shared square\n const N = Math.min(Y, X);\n for (let y = 1; y < N; ++y) {\n for (let x = 0; x < y; ++x) {\n const temp = matrix[y][x];\n matrix[y][x] = matrix[x][y];\n matrix[x][y] = temp;\n }\n }\n\n // Add columns\n if (Y > X) {\n for (let y = 0; y < X; ++y) {\n const row = matrix[y];\n row.length = Y;\n for (let x = X; x < Y; ++x) {\n row[x] = matrix[x][y];\n }\n }\n matrix.length = X;\n }\n\n // Add rows\n if (Y < X) {\n matrix.length = X;\n for (let y = Y; y < X; ++y) {\n const row = new Array(Y);\n for (let x = 0; x < Y; ++x) {\n row[x] = matrix[x][y];\n }\n matrix[y] = row;\n }\n for (let y = 0; y < Y; ++y) {\n matrix[y].length = Y;\n }\n }\n}\n","import type { Matrix } from \"./types/matrix.ts\";\nimport type { MatrixLike } from \"./types/matrixLike.ts\";\n\nimport { create, from, gen, invert, negate } from \"./utils/matrix.ts\";\nimport { getMax, getMin } from \"./utils/matrixLike.ts\";\n\n/**\n * Creates a copy from a given matrix or matrix-like input.\n *\n * @param matrix - The matrix to be copied.\n *\n * @returns A copy of the given matrix.\n */\nexport function copyMatrix<T>(matrix: MatrixLike<T>): Matrix<T> {\n return from(matrix);\n}\n\n/**\n * Constructs a matrix from a set of row\n * and column objects using a provided callback function.\n *\n * @param rows - An array of row objects (such as workers).\n * @param cols - An array of column objects (such as jobs).\n * @param callbackFn - Given a row and a column, returns a value.\n *\n * @returns A matrix where the values at position `[r][c]`\n * represent the value derived from row `r` and column `c`.\n *\n * @example\n * ```typescript\n * // Define workers, jobs, and a simple cost function\n * const workers = ['Alice', 'Bob'];\n * const jobs = ['Job1', 'Job2'];\n * const costFn = (worker: string, job: string) => worker.length + job.length;\n *\n * // Create a cost matrix\n * const costs = createMatrix(workers, jobs, costFn);\n * // [\n * // [9, 9], // ['Alice' + 'Job1', 'Alice' + 'Job2']\n * // [7, 7] // [ 'Bob' + 'Job1', 'Bob' + 'Job2']\n * // ]\n * ```\n */\nexport function createMatrix<R, C, T>(\n rows: ArrayLike<R>,\n cols: ArrayLike<C>,\n callbackFn: (row: R, col: C) => T,\n): Matrix<T> {\n return create(rows, cols, callbackFn);\n}\n\n/**\n * Constructs a matrix with given dimensions\n * using a provided callback function.\n *\n * @param rows - The number of rows in the matrix.\n * @param cols - The number of columns in the matrix.\n * @param callbackFn - Given row and column indices, returns a value.\n *\n * @returns A matrix where the values at position `[r][c]`\n * represent the value derived from row `r` and column `c`.\n *\n * @example\n * ```typescript\n * // Define workers, jobs, and a simple cost function\n * const workers = ['Alice', 'Bob'];\n * const jobs = ['Job1', 'Job2'];\n * const costFn = (w: number, j: number) => workers[w].length + jobs[j].length;\n *\n * // Create a cost matrix\n * const costs = createMatrix(workers.length, jobs.length, costFn);\n * // [\n * // [9, 9], // ['Alice' + 'Job1', 'Alice' + 'Job2']\n * // [7, 7] // [ 'Bob' + 'Job1', 'Bob' + 'Job2']\n * // ]\n * ```\n */\nexport function genMatrix<T>(\n rows: number,\n cols: number,\n callbackFn: (row: number, col: number) => T,\n): Matrix<T> {\n return gen(rows, cols, callbackFn);\n}\n\n/**\n * Finds the maximum value in a given matrix.\n *\n * @param matrix - The matrix.\n *\n * @returns The maximum value, or `undefined` if the matrix is empty.\n */\nexport function getMatrixMax(matrix: MatrixLike<number>): number | undefined;\nexport function getMatrixMax(matrix: MatrixLike<bigint>): bigint | undefined;\nexport function getMatrixMax<T extends number | bigint>(\n matrix: MatrixLike<T>,\n): T | undefined {\n return getMax(matrix as MatrixLike<number>) as T | undefined;\n}\n\n/**\n * Finds the minimum value in a given matrix.\n *\n * @param matrix - The matrix.\n *\n * @returns The minimum value, or `undefined` if the matrix is empty.\n */\nexport function getMatrixMin(matrix: MatrixLike<number>): number | undefined;\nexport function getMatrixMin(matrix: MatrixLike<bigint>): bigint | undefined;\nexport function getMatrixMin<T extends number | bigint>(\n matrix: MatrixLike<T>,\n): T | undefined {\n return getMin(matrix as MatrixLike<number>) as T | undefined;\n}\n\n/**\n * Inverts the values in a given matrix by\n * subtracting each element from a specified large value.\n *\n * This is useful for converting a profit matrix\n * into a cost matrix, or vice versa.\n *\n * @param matrix - The cost matrix to be inverted. Modified in place.\n * @param bigVal - (Optional) A large value used as the basis for inversion.\n * If not provided, the maximum value in the matrix is used.\n *\n * @example\n * const matrix = [\n * [1, 2, 3],\n * [4, 5, 6]\n * ];\n *\n * // Invert the matrix\n * invertMatrix(matrix);\n *\n * // matrix is now:\n * // [\n * // [5, 4, 3],\n * // [2, 1, 0]\n * // ]\n *\n * @example\n * const matrix = [\n * [10, 20],\n * [30, 40]\n * ];\n *\n * // Invert the matrix with a given bigVal\n * invertMatrix(matrix, 50);\n *\n * // matrix is now:\n * // [\n * // [40, 30],\n * // [20, 10]\n * // ]\n */\nexport function invertMatrix(matrix: Matrix<number>, bigVal?: number): void;\nexport function invertMatrix(matrix: Matrix<bigint>, bigVal?: bigint): void;\nexport function invertMatrix<T extends number | bigint>(\n matrix: Matrix<T>,\n bigVal?: T,\n): void {\n invert(matrix as Matrix<number>, bigVal as number);\n}\n\n/**\n * Negates the values in a given matrix.\n *\n * This is useful for converting a profit matrix\n * into a cost matrix, or vice versa.\n *\n * @param matrix - The matrix to be negated. Modified in place.\n *\n * @example\n * const matrix = [\n * [1, 2, 3],\n * [4, -5, 6],\n * [7, 8, 9]\n * ];\n *\n * // Negate the matrix\n * negateMatrix(matrix);\n *\n * // matrix is now:\n * // [\n * // [-1, -2, -3],\n * // [-4, 5, -6],\n * // [-7, -8, -9]\n * // ]\n */\nexport function negateMatrix(matrix: Matrix<number>): void;\nexport function negateMatrix(matrix: Matrix<bigint>): void;\nexport function negateMatrix<T extends number | bigint>(\n matrix: Matrix<T>,\n): void {\n negate(matrix);\n}\n","/**\n * Checks if the given value is of type `bigint`.\n *\n * @param value - The value to check.\n *\n * @returns `true` if the value is of type `bigint`, `false` otherwise.\n *\n * @example\n * console.log(isBigInt(10n)); // true\n *\n * @example\n * console.log(isBigInt(10)); // false\n */\nexport function isBigInt(value: unknown): value is bigint {\n return typeof value === \"bigint\";\n}\n\n/**\n * Checks if the given value is of type `number`.\n *\n * @param value - The value to check.\n *\n * @returns `true` if the value is of type `number`, `false` otherwise.\n *\n * @example\n * console.log(isNumber(10)); // true\n *\n * @example\n * console.log(isNumber(10n)); // false\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === \"number\";\n}\n","/**\n * Transforms the given array into an array of key, value pairs\n * for every entry in the array.\n *\n * @param array - The array to transform into entries.\n *\n * @returns An array of key, value pairs for every entry in the array.\n *\n * @example\n * entries(['a', 'b', 'c']);\n * // Returns [[0, 'a'], [1, 'b'], [2, 'c']]\n */\nexport function entries<T>(array: ArrayLike<T>): [number, T][] {\n const N = array.length;\n const out = new Array(N);\n for (let i = 0; i < N; ++i) {\n out[i] = [i, array[i]];\n }\n return out;\n}\n\n/**\n * Find the minimum value in a given array.\n *\n * @param array - An array.\n *\n * @returns The minimum value, or `undefined` if the array is empty.\n *\n * @example\n * const array = [3, 1, 2];\n * console.log(getMin(array)); // Output: 1\n *\n * @example\n * const array = [3n, 1n, 2n];\n * console.log(getMin(array)); // Output: 1n\n *\n * @example\n * const array = ['d', 'b', 'c'];\n * console.log(getMin(array)); // Output: 'b'\n */\nexport function getMin(array: ArrayLike<number>): number | undefined;\nexport function getMin(array: ArrayLike<bigint>): bigint | undefined;\nexport function getMin(array: ArrayLike<string>): string | undefined;\nexport function getMin<T extends number | bigint | string>(\n array: ArrayLike<T>,\n): T | undefined {\n const N = array.length;\n if (N <= 0) {\n return undefined;\n }\n\n let min = array[0];\n for (let i = 1; i < N; ++i) {\n if (min > array[i]) {\n min = array[i];\n }\n }\n\n return min;\n}\n","import type { MutableArrayLike } from \"../types/mutableArrayLike.ts\";\n\n/**\n * Partitions an array of indices based on the minimum value of the indices in another array.\n *\n * @param indices - The array of indices to be partitioned. Modified in place.\n * @param values - The array from which index values are read.\n * @param min - The starting position in `indices` for partitioning (inclusive). Defaults to 0.\n * @param max - The ending position in `indices` for partitioning (exclusive). Defaults to `indices.length`.\n *\n * @returns The position one more than the partitioned portion of `indices`. Indices in the partition\n * have values equal to the minimum value of the scanned indices.\n *\n * @example\n * ```javascript\n * const indices = [0, 1, 2, 3, 4];\n * const values = [5, 3, 2, 4, 1];\n * const mid = partitionByMin(indices, values);\n * console.log(indices.slice(0, mid));\n * // Outputs: [4]\n * // Explanation: 1 is the minimum in `values`, found only in index 4.\n * ```\n *\n * @example\n * ```javascript\n * const indices = [0, 5, 3, 2, 4, 1];\n * const values = [10, 20, 80, 50, 30, 50];\n * const mid = partitionByMin(indices, values, 1, 4);\n * console.log(indices.slice(1, mid));\n * // Outputs: [5, 3]\n * // Explanation: The function scans the `indices` subarray from positions [1, 4); [5, 3, 2].\n * // These indices map to values 50, 50 and 80 in the `values` array, respectively. The\n * // minimum value is 50, which both index 5 and 3 are equal to. Thus, indices 5 and 3 are\n * // partitioned into the beginning of the subarray, in the order they are encountered.\n * ```\n */\nexport function partitionByMin<T extends number | bigint | string>(\n indices: MutableArrayLike<number>,\n values: ArrayLike<T>,\n min = 0,\n max = indices.length,\n): number {\n let mid = min + 1;\n let minIndex = indices[min];\n\n for (let pos = mid; pos < max; ++pos) {\n const index = indices[pos];\n if (values[index] > values[minIndex]) {\n continue;\n }\n if (values[index] < values[minIndex]) {\n minIndex = index;\n mid = min;\n }\n indices[pos] = indices[mid];\n indices[mid++] = index;\n }\n\n return mid;\n}\n","import type { MatrixLike } from \"../types/matrixLike.ts\";\nimport type { MutableArrayLike } from \"../types/mutableArrayLike.ts\";\n\nimport { partitionByMin } from \"../utils/mutableArrayLike.ts\";\n\n/**\n * This step iteratively improves upon an initial matching until a complete\n * matching is found. This involves updating dual variables and managing\n * slack values to uncover new opportunities for optimal assignments.\n *\n * @param unmatched - The number of missing matches.\n * @param mat - An MxN cost matrix.\n * @param dualX - The dual variables for each matrix column. Modified in place.\n * @param dualY - The dual variables for each matrix row. Modified in place.\n * @param starsX - An array mapping star columns to row. Modified in place.\n * @param starsY - An array mapping star rows to columns. Modified in place.\n */\nexport function step4B(\n unmatched: number,\n matrix: MatrixLike<number>,\n dualX: MutableArrayLike<number>,\n dualY: MutableArrayLike<number>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): void;\nexport function step4B(\n unmatched: number,\n matrix: MatrixLike<bigint>,\n dualX: MutableArrayLike<bigint>,\n dualY: MutableArrayLike<bigint>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): void;\nexport function step4B<T extends number | bigint>(\n unmatched: number,\n matrix: MatrixLike<T>,\n dualX: MutableArrayLike<T>,\n dualY: MutableArrayLike<T>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): void {\n // If no unmatched column\n if (unmatched <= 0) {\n return;\n }\n\n const Y = dualY.length;\n const slack = new Uint32Array(Y);\n const slackV = new Array<T>(Y);\n const slackX = new Uint32Array(Y);\n\n // Match unmatched columns\n for (let x = 0; unmatched > 0; ++x) {\n if (starsX[x] !== -1) {\n continue;\n }\n\n // @ts-expect-error ts(2769)\n const N = matchB(x, matrix, dualX, dualY, starsY, slack, slackV, slackX);\n --unmatched;\n\n // Update dual variables\n // @ts-expect-error ts(2769)\n step6B(x, N, dualX, dualY, slack, slackV, starsY);\n\n // Update matching\n step5B(slack[N - 1], slackX, starsX, starsY);\n }\n}\n\n/**\n * Augments the current matching.\n *\n * This step effectively increases the number of matches (stars)\n * by 1, bringing the algorithm closer to an optimal assignment.\n *\n * Augmentation is performed by flipping matched and unmatched edges along\n * an augmenting path, starting from an unmatched node / edge and\n * continuing until no matched edge can be found.\n *\n * @param y - The starting node's row.\n * @param primeY - An array mapping primed rows to columns.\n * @param starsX - An array mapping star columns to row. Modified in place.\n * @param starsY - An array mapping star rows to columns. Modified in place.\n */\nexport function step5B(\n y: number,\n primeY: ArrayLike<number>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): void {\n do {\n const x = primeY[y];\n const sy = starsX[x];\n starsX[x] = y;\n starsY[y] = x;\n y = sy;\n } while (y !== -1);\n}\n\n/**\n * Adjusts dual variables to uncover more admissible edges.\n *\n * @param x - The starting node's column.\n * @param N - The number of adjustments to make.\n * @param dualX - The dual variables for each matrix column. Modified in place.\n * @param dualY - The dual variables for each matrix row. Modified in place.\n * @param slack - An array of covered row indices.\n * @param slackV - The slack values for each row.\n * @param starsY - An array mapping star rows to columns.\n */\nexport function step6B(\n x: number,\n N: number,\n dualX: MutableArrayLike<number>,\n dualY: MutableArrayLike<number>,\n slack: ArrayLike<number>,\n slackV: ArrayLike<number>,\n starsY: ArrayLike<number>,\n): void;\nexport function step6B(\n x: number,\n N: number,\n dualX: MutableArrayLike<bigint>,\n dualY: MutableArrayLike<bigint>,\n slack: ArrayLike<number>,\n slackV: ArrayLike<bigint>,\n starsY: ArrayLike<number>,\n): void;\nexport function step6B<T extends number | bigint>(\n x: number,\n N: number,\n dualX: MutableArrayLike<T>,\n dualY: MutableArrayLike<T>,\n slack: ArrayLike<number>,\n slackV: ArrayLike<T>,\n starsY: ArrayLike<number>,\n): void {\n const sum = slackV[slack[N - 1]];\n\n let min = sum;\n for (let i = 0; i < N; ++i) {\n const y = slack[i];\n // @ts-expect-error ts(2365)\n dualX[x] += min;\n min = (sum - slackV[y]) as T;\n // @ts-expect-error ts(2322)\n dualY[y] -= min;\n x = starsY[y];\n }\n}\n\n/**\n * Matches a given unmatched column to an unmatched row.\n *\n * @param x - An unmatched column.\n * @param matrix - An MxN cost matrix.\n * @param dualX - The dual variables for each matrix column.\n * @param dualY - The dual variables for each matrix row.\n * @param starsY - An array mapping star rows to columns.\n * @param slack - An array of covered row indices. Modified in place.\n * @param slackV - The slack values for each row. Modified in place.\n * @param slackX - An array mapping a slack row to column. Modified in place.\n */\nexport function matchB(\n x: number,\n matrix: MatrixLike<number>,\n dualX: ArrayLike<number>,\n dualY: ArrayLike<number>,\n starsY: ArrayLike<number>,\n slack: MutableArrayLike<number>,\n slackV: MutableArrayLike<number>,\n slackX: MutableArrayLike<number>,\n): number;\nexport function matchB(\n x: number,\n matrix: MatrixLike<bigint>,\n dualX: ArrayLike<bigint>,\n dualY: ArrayLike<bigint>,\n starsY: ArrayLike<number>,\n slack: MutableArrayLike<number>,\n slackV: MutableArrayLike<bigint>,\n slackX: MutableArrayLike<number>,\n): number;\nexport function matchB<T extends number | bigint>(\n x: number,\n matrix: MatrixLike<T>,\n dualX: ArrayLike<T>,\n dualY: ArrayLike<T>,\n starsY: ArrayLike<number>,\n slack: MutableArrayLike<number>,\n slackV: MutableArrayLike<T>,\n slackX: MutableArrayLike<number>,\n): number {\n const Y = slack.length;\n\n // Initialize slack\n let dx = dualX[x];\n for (let y = 0; y < Y; ++y) {\n slack[y] = y;\n slackV[y] = (matrix[y][x] - dualY[y] - dx) as T;\n slackX[y] = x;\n }\n\n // Initialize zeros\n let zeros = partitionByMin(slack, slackV, 0);\n let zero = slackV[slack[0]];\n\n // Grow a hungarian tree until an augmenting path is found\n let steps = 1;\n for (let y = slack[0]; starsY[y] !== -1; y = slack[steps++]) {\n // Update slack\n x = starsY[y];\n dx = (dualX[x] - zero) as T;\n for (let i = zeros; i < Y; ++i) {\n y = slack[i];\n const value = (matrix[y][x] - dualY[y] - dx) as T;\n if (value >= slackV[y]) {\n continue;\n }\n slackX[y] = x;\n slackV[y] = value;\n if (value === zero) {\n slack[i] = slack[zeros];\n slack[zeros++] = y;\n }\n }\n\n // Update zeros\n if (steps >= zeros) {\n zeros = partitionByMin(slack, slackV, zeros);\n zero = slackV[slack[steps]];\n }\n }\n\n return steps;\n}\n","import type { MatrixLike } from \"../types/matrixLike.ts\";\nimport type { Matching } from \"../types/matching.ts\";\nimport type { MutableArrayLike } from \"../types/mutableArrayLike.ts\";\n\nimport { getMin } from \"../utils/arrayLike.ts\";\nimport { isBigInt } from \"../utils/is.ts\";\nimport { partitionByMin } from \"../utils/mutableArrayLike.ts\";\n\nimport { step4B } from \"./bigMunkresB.ts\";\n\nexport function exec(matrix: MatrixLike<number>): Matching<number>;\nexport function exec(matrix: MatrixLike<bigint>): Matching<bigint>;\nexport function exec<T extends number | bigint>(\n matrix: MatrixLike<T>,\n): Matching<T> {\n // Get dimensions\n const Y = matrix.length;\n const X = matrix[0]?.length ?? 0;\n\n // Check if empty matrix\n if (Y <= 0 || X <= 0) {\n return { dualX: [], dualY: [], matrix, starsX: [], starsY: [] };\n }\n\n // Step 1: Reduce\n const dualX = new Array<T>(X);\n const dualY = new Array<T>(Y);\n // @ts-expect-error ts(2769)\n step1(matrix, dualX, dualY);\n\n // Steps 2 & 3: Find initial matching\n const starsX = new Array<number>(X).fill(-1);\n const starsY = new Array<number>(Y).fill(-1);\n // @ts-expect-error ts(2769)\n const stars = steps2To3(matrix, dualX, dualY, starsX, starsY);\n\n // Step 4: Find complete matching\n Y <= X\n ? // @ts-expect-error ts(2769)\n step4(Y - stars, matrix, dualX, dualY, starsX, starsY)\n : // @ts-expect-error ts(2769)\n step4B(X - stars, matrix, dualX, dualY, starsX, starsY);\n\n // Return matching\n return { dualX, dualY, matrix, starsX, starsY };\n}\n\n/**\n * Initializes the dual variables for the Munkres algorithm.\n *\n * This is a preprocessing step that effectively performs\n * row-wise and column-wise reductions on the cost matrix. This\n * helps find an initial matching and improves the efficiency\n * of subsequent steps.\n *\n * @param matrix - The cost matrix.\n * @param dualX - The dual variables for each matrix column. Modified in place.\n * @param dualY - The dual variables for each matrix row. Modified in place.\n */\nexport function step1(\n matrix: MatrixLike<number>,\n dualX: MutableArrayLike<number>,\n dualY: MutableArrayLike<number>,\n): void;\nexport function step1(\n matrix: MatrixLike<bigint>,\n dualX: MutableArrayLike<bigint>,\n dualY: MutableArrayLike<bigint>,\n): void;\nexport function step1<T extends number | bigint>(\n matrix: MatrixLike<T>,\n dualX: MutableArrayLike<T>,\n dualY: MutableArrayLike<T>,\n): void {\n const X = dualX.length;\n const Y = dualY.length;\n\n // If matrix is tall, skip row reduction\n if (Y > X) {\n dualY.fill((isBigInt(matrix[0][0]) ? 0n : 0) as T);\n } else {\n // Reduce rows\n for (let y = 0; y < Y; ++y) {\n // @ts-expect-error ts(2769)\n dualY[y] = getMin(matrix[y])!;\n }\n }\n\n // If matrix is wide, skip column reduction\n if (Y < X) {\n dualX.fill((isBigInt(matrix[0][0]) ? 0n : 0) as T);\n return;\n }\n\n // Initialize dualX\n let dy = dualY[0];\n let row = matrix[0];\n for (let x = 0; x < X; ++x) {\n dualX[x] = (row[x] - dy) as T;\n }\n\n // Reduce columns\n for (let y = 1; y < Y; ++y) {\n dy = dualY[y];\n row = matrix[y];\n for (let x = 0; x < X; ++x) {\n const dx = row[x] - dy;\n if (dx < dualX[x]) {\n dualX[x] = dx as T;\n }\n }\n }\n}\n\n/**\n * Finds an initial matching for the munkres algorithm.\n *\n * @param matrix - The cost matrix.\n * @param dualX - The dual variables for each matrix column.\n * @param dualY - The dual variables for each matrix row.\n * @param starsX - An array mapping star columns to row. Modified in place.\n * @param starsY - An array mapping star rows to columns. Modified in place.\n *\n * @returns The number of matches (stars) found.\n */\nexport function steps2To3(\n matrix: MatrixLike<number>,\n dualX: ArrayLike<number>,\n dualY: ArrayLike<number>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): number;\nexport function steps2To3(\n matrix: MatrixLike<bigint>,\n dualX: ArrayLike<bigint>,\n dualY: ArrayLike<bigint>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): number;\nexport function steps2To3<T extends number | bigint>(\n matrix: MatrixLike<T>,\n dualX: ArrayLike<T>,\n dualY: ArrayLike<T>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): number {\n const X = dualX.length;\n const Y = dualY.length;\n const S = Y <= X ? Y : X;\n\n let stars = 0;\n for (let y = 0; y < Y && stars < S; ++y) {\n const dy = dualY[y];\n const row = matrix[y];\n for (let x = 0; x < X; ++x) {\n if (starsX[x] === -1 && dy === row[x] - dualX[x]) {\n starsX[x] = y;\n starsY[y] = x;\n ++stars;\n break;\n }\n }\n }\n\n return stars;\n}\n\n/**\n * This step iteratively improves upon an initial matching until a complete\n * matching is found. This involves updating dual variables and managing\n * slack values to uncover new opportunities for optimal assignments.\n *\n * @param unmatched - The number of missing matches.\n * @param mat - An MxN cost matrix.\n * @param dualX - The dual variables for each matrix column. Modified in place.\n * @param dualY - The dual variables for each matrix row. Modified in place.\n * @param starsX - An array mapping star columns to row. Modified in place.\n * @param starsY - An array mapping star rows to columns. Modified in place.\n */\nexport function step4(\n unmatched: number,\n matrix: MatrixLike<number>,\n dualX: MutableArrayLike<number>,\n dualY: MutableArrayLike<number>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): void;\nexport function step4(\n unmatched: number,\n matrix: MatrixLike<bigint>,\n dualX: MutableArrayLike<bigint>,\n dualY: MutableArrayLike<bigint>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): void;\nexport function step4<T extends number | bigint>(\n unmatched: number,\n matrix: MatrixLike<T>,\n dualX: MutableArrayLike<T>,\n dualY: MutableArrayLike<T>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): void {\n // If no unmatched row\n if (unmatched <= 0) {\n return;\n }\n\n const X = dualX.length;\n const slack = new Uint32Array(X);\n const slackV = new Array<T>(X);\n const slackY = new Uint32Array(X);\n\n // Match unmatched rows\n for (let y = 0; unmatched > 0; ++y) {\n if (starsY[y] !== -1) {\n continue;\n }\n\n // @ts-expect-error ts(2769)\n const N = match(y, matrix, dualX, dualY, starsX, slack, slackV, slackY);\n --unmatched;\n\n // Update dual variables\n // @ts-expect-error ts(2769)\n step6(y, N, dualX, dualY, slack, slackV, starsX);\n\n // Update matching\n step5(slack[N - 1], slackY, starsX, starsY);\n }\n}\n\n/**\n * Augments the current matching.\n *\n * This step effectively increases the number of matches (stars)\n * by 1, bringing the algorithm closer to an optimal assignment.\n *\n * Augmentation is performed by flipping matched and unmatched edges along\n * an augmenting path, starting from an unmatched node / edge and\n * continuing until no matched edge can be found.\n *\n * @param x - The starting node's column.\n * @param primeX - An array mapping primed columns to rows.\n * @param starsX - An array mapping star columns to row. Modified in place.\n * @param starsY - An array mapping star rows to columns. Modified in place.\n */\nexport function step5(\n x: number,\n primeX: ArrayLike<number>,\n starsX: MutableArrayLike<number>,\n starsY: MutableArrayLike<number>,\n): void {\n do {\n const y = primeX[x];\n const sx = starsY[y];\n starsX[x] = y;\n starsY[y] = x;\n x = sx;\n } while (x !== -1);\n}\n\n/**\n * Adjusts dual variables to uncover more admissible edges.\n *\n * @param y - The starting node's row.\n * @param N - The number of adjustments to make.\n * @param dualX - The dual variables for each matrix column. Modified in place.\n * @param dualY - The dual variables for each matrix row. Modified in place.\n * @param slack - An array of covered column indices.\n * @param slackV - The slack values for each column.\n * @param starsX - An array mapping star columns to row.\n */\nexport function step6(\n y: number,\n N: number,\n dualX: MutableArrayLike<number>,\n dualY: MutableArrayLike<number>,\n slack: ArrayLike<number>,\n slackV: ArrayLike<number>,\n starsX: ArrayLike<number>,\n): void;\nexport function step6(\n y: number,\n N: number,\n dualX: MutableArrayLike<bigint>,\n dualY: MutableArrayLike<bigint>,\n slack: ArrayLike<number>,\n slackV: ArrayLike<bigint>,\n starsX: ArrayLike<number>,\n): void;\nexport function step6<T extends number | bigint>(\n y: number,\n N: number,\n dualX: MutableArrayLike<T>,\n dualY: MutableArrayLike<T>,\n slack: ArrayLike<number>,\n slackV: ArrayLike<T>,\n starsX: ArrayLike<number>,\n): void {\n const sum = slackV[slack[--N]];\n // @ts-expect-error ts(2365)\n dualY[y] += sum;\n\n for (let i = 0; i < N; ++i) {\n const x = slack[i];\n y = starsX[x];\n const min = sum - slackV[x];\n // @ts-expect-error ts(2322)\n dualX[x] -= min;\n // @ts-expect-error ts(2365)\n dualY[y] += min;\n }\n}\n\n/**\n * Matches a given unmatched row to an unmatched column.\n *\n * @param y - An unmatched row.\n * @param matrix - An MxN cost matrix.\n * @param dualX - The dual variables for each matrix column.\n * @param dualY - The dual variables for each matrix row.\n * @param starsX - An array mapping star columns to row.\n * @param slack - An array of covered column indices. Modified in place.\n * @param slackV - The slack values for each column. Modified in place.\n * @param slackY - An array mapping a slack column to row. Modified in place.\n */\nexport function match(\n y: number,\n matrix: MatrixLike<number>,\n dualX: ArrayLike<number>,\n dualY: ArrayLike<number>,\n starsX: ArrayLike<number>,\n slack: MutableArrayLike<number>,\n slackV: MutableArrayLike<number>,\n slackY: MutableArrayLike<number>,\n): number;\nexport function match(\n y: number,\n matrix: MatrixLike<bigint>,\n dualX: ArrayLike<bigint>,\n dualY: ArrayLike<bigint>,\n starsX: ArrayLike<number>,\n slack: MutableArrayLike<number>,\n slackV: MutableArrayLike<bigint>,\n slackY: MutableArrayLike<number>,\n): number;\nexport function match<T extends number | bigint>(\n y: number,\n matrix: MatrixLike<T>,\n dualX: ArrayLike<T>,\n dualY: ArrayLike<T>,\n starsX: ArrayLike<number>,\n slack: MutableArrayLike<number>,\n slackV: MutableArrayLike<T>,\n slackY: MutableArrayLike<number>,\n): number {\n const X = slack.length;\n\n // Initialize slack\n let dy = dualY[y];\n let row = matrix[y];\n for (let x = 0; x < X; ++x) {\n slack[x] = x;\n slackV[x] = (row[x] - dualX[x] - dy) as T;\n slackY[x] = y;\n }\n\n // Initialize zeros\n let zeros = partitionByMin(slack, slackV, 0);\n let zero = slackV[slack[0]];\n\n // Grow a hungarian tree until an augmenting path is found\n let steps = 1;\n for (let x = slack[0]; starsX[x] !== -1; x = slack[steps++]) {\n // Update slack\n y = starsX[x];\n dy = (dualY[y] - zero) as T;\n row = matrix[y];\n for (let i = zeros; i < X; ++i) {\n x = slack[i];\n const value = (row[x] - dualX[x] - dy) as T;\n if (value >= slackV[x]) {\n continue;\n }\n slackY[x] = y;\n slackV[x] = value;\n if (value === zero) {\n slack[i] = slack[zeros];\n slack[zeros++] = x;\n }\n }\n\n // Update zeros\n if (steps >= zeros) {\n zeros = partitionByMin(slack, slackV, zeros);\n zero = slackV[slack[steps]];\n }\n }\n\n return steps;\n}\n","import type { MatrixLike } from \"../types/matrixLike.ts\";\nimport type { MutableArrayLike } from \"../types/mutableArrayLike.ts\";\n\nimport { partitionByMin } from \"../utils/mutableArrayLike.ts\";\n\nimport { step5B } from \"./bigMunkresB.ts\";\n\n/**\n * This step iteratively improves upon an initial matching until a complete\n * matching is found. This involves updating dual variables and managing\n * slack values to uncover new opportunities for optimal assignments.\n *\n * @param unmatched - The number of missing matches.\n * @param mat - An MxN cost matrix.\n * @param dualX - The dual variables for each matrix column. Modified in place.\n * @param dualY - The dual variables for each matrix row. Modified in place.\n * @param starsX - An array mapping star columns to row. Modified in place.\n * @param starsY - An array mapping star rows to columns. Modified in place.\n */\nexport function step4B(\n unmatched: number,\n matrix: MatrixLike<number>,\n dualX: number[],\n dualY: number[],\n starsX: number[],\n starsY: number[],\n): void {\n // If no unmatched column\n if (unmatched <= 0) {\n return;\n }\n\n const Y = dualY.length;\n const slack = new Uint32Array(Y);\n const slackV = new Array<number>(Y);\n const slackX = new Uint32Array(Y);\n\n // Match unmatched columns\n for (let x = 0; unmatched > 0; ++x) {\n if (starsX[x] !== -1) {\n continue;\n }\n\n const N = matchB(x, matrix, dualX, dualY, starsY, slack, slackV, slackX);\n --unmatched;\n\n // Update dual variables\n step6B(x, N, dualX, dualY, slack, slackV, starsY);\n\n // Update matching\n step5B(slack[N - 1], slackX, starsX, starsY);\n }\n}\n\n/**\n * Adjusts dual variables to uncover more admissible edges.\n *\n * @param x - The starting node's column.\n * @param N - The number of adjustments to make.\n * @param dualX - The dual variables for each matrix column. Modified in place.\n * @param dualY - The dual variables for each matrix row. Modified in place.\n * @param slack - An array of covered row indices.\n * @param slackV - The slack values for each row.\n * @param starsY - An array mapping star rows to columns.\n */\nexport function step6B(\n x: number,\n N: number,\n dualX: number[],\n dualY: number[],\n slack: ArrayLike<number>,\n slackV: ArrayLike<number>,\n starsY: ArrayLike<number>,\n): void {\n const sum = slackV[slack[N - 1]];\n\n let min = sum;\n for (let i = 0; i < N; ++i) {\n const y = slack[i];\n dualX[x] = dualX[x] + min || 0;\n min = sum - slackV[y] || 0;\n dualY[y] = dualY[y] - min || 0;\n x = starsY[y];\n }\n}\n\n/**\n * Matches a given unmatched column to an unmatched row.\n *\n * @param x - An unmatched column.\n * @param matrix - An MxN cost matrix.\n * @param dualX - The dual variables for each matrix column.\n * @param dualY - The dual variables for each matrix row.\n * @param starsY - An array mapping star rows to columns.\n * @param slack - An array of covered row indices. Modified in place.\n * @param slackV - The slack values for each row. Modified in place.\n * @param slackX - An array mapping a slack row to column. Modified in place.\n */\nexport function matchB(\n x: number,\n matrix: MatrixLike<number>,\n dualX: number[],\n dualY: number[],\n starsY: number[],\n slack: MutableArrayLike<number>,\n slackV: MutableArrayLike<number>,\n slackX: MutableArrayLike<number>,\n): number {\n const Y = slack.length;\n\n // Initialize slack\n let dx = dualX[x];\n for (let y = 0; y < Y; ++y) {\n slack[y] = y;\n slackV[y] = matrix[y][x] - (dx + dualY[y] || 0) || 0;\n slackX[y] = x;\n }\n\n // Initialize zeros\n let zeros = partitionByMin(slack, slackV, 0);\n let zero = slackV[slack[0]];\n\n // Grow a hungarian tree until an augmenting path is found\n let steps = 1;\n for (let y = slack[0]; starsY[y] !== -1; y = slack[steps++]) {\n // Update slack\n x = starsY[y];\n dx = dualX[x];\n for (let i = zeros; i < Y; ++i) {\n y = slack[i];\n const value = (matrix[y][x] - (dx + dualY[y] || 0) || 0) + zero || 0;\n if (value >= slackV[y]) {\n continue;\n }\n slackX[y] = x;\n slackV[y] = value;\n if (value === zero) {\n slack[i] = slack[zeros];\n slack[zeros++] = y;\n }\n }\n\n // Update zeros\n if (steps >= zeros) {\n zeros = partitionByMin(slack, slackV, zeros);\n zero = slackV[slack[steps]];\n }\n