import * as utils from "./utils.js";
import * as wasm from "./wasm.js";
/**
* Perform a hypergeometric test, typically for over-enrichment of markers across gene sets.
* This can be computed for multiple gene sets by providing arrays as some or all of the arguments.
* If multiple arrays are supplied, they must be of the same length.
*
* @param {number|Array|TypedArray|WasmArray} markersInSet - Number of detected markers that are also in the gene set.
* @param {number|Array|TypedArray|WasmArray} numberOfMarkers - Total number of detected markers.
* @param {number|Array|TypedArray|WasmArray} geneSetSize - Size of the gene set.
* @param {number|Array|TypedArray|WasmArray} numberOfGenes - Total number of genes.
* @param {object} [options={}] - Optional parameters.
* @param {boolean} [options.asTypedArray=true] - Whether to return a Float64Array.
* If `false`, a Float64WasmArray is returned instead.
* @param {?Float64WasmArray} [options.buffer=null] - Buffer in which to store the output.
* If not `null`, this should have the same length as any of the array-like arguments.
* @param {boolean} [options.log=false] - Whether to compute log-probabilities.
* @param {?number} [options.numberOfThreads=null] - Number of threads to use.
* If `null`, defaults to {@linkcode maximumThreads}.
*
* @return {Float64Array|Float64WasmArray} An array of length equal to that of the supplied arrays (or 1, if no arrays are supplied).
* The i-th entry contains the p-value for enrichment computed using the i-th entry of each supplied array.
* If `buffer` is supplied, the function returns `buffer` if `asTypedArray = false`, or a view on `buffer` if `asTypedArray = true`.
*/
export function hypergeometricTest(markersInSet, numberOfMarkers, geneSetSize, numberOfGenes, options = {}) {
let { asTypedArray = true, buffer = null, log = false, numberOfThreads = null, ...others } = options;
utils.checkOtherOptions(others);
let markersInSet_data;
let numberOfMarkers_data;
let geneSetSize_data;
let numberOfGenes_data;
let nthreads = utils.chooseNumberOfThreads(numberOfThreads);
let ntests = null;
let check_length = (candidate, name, sofar) => {
if (sofar !== null && candidate.length !== sofar) {
throw new Error("array inputs must have the same length (failing for '" + name + "')");
}
return candidate.length;
}
if (typeof markersInSet == "number") {
markersInSet = [markersInSet];
} else {
ntests = check_length(markersInSet, "markersInSet", ntests);
}
if (typeof numberOfMarkers == "number") {
numberOfMarkers = [numberOfMarkers];
} else {
ntests = check_length(numberOfMarkers, "numberOfMarkers", ntests);
}
if (typeof geneSetSize == "number") {
geneSetSize = [geneSetSize];
} else {
ntests = check_length(geneSetSize, "geneSetSize", ntests);
}
if (typeof numberOfGenes == "number") {
numberOfGenes = [numberOfGenes];
} else {
ntests = check_length(numberOfGenes, "numberOfGenes", ntests);
}
if (ntests == null) {
ntests = 1;
}
let tmp = null;
try {
markersInSet_data = utils.wasmifyArray(markersInSet, "Int32WasmArray");
numberOfMarkers_data = utils.wasmifyArray(numberOfMarkers, "Int32WasmArray");
geneSetSize_data = utils.wasmifyArray(geneSetSize, "Int32WasmArray");
numberOfGenes_data = utils.wasmifyArray(numberOfGenes, "Int32WasmArray");
if (buffer == null) {
buffer = utils.createFloat64WasmArray(ntests);
tmp = buffer;
}
wasm.call(module => module.hypergeometric_test(
ntests,
markersInSet_data.length != 1,
markersInSet_data.offset,
geneSetSize_data.length != 1,
geneSetSize_data.offset,
numberOfMarkers_data.length != 1,
numberOfMarkers_data.offset,
numberOfGenes_data.length != 1,
numberOfGenes_data.offset,
buffer.offset,
log,
nthreads
));
} finally {
utils.free(markersInSet_data);
utils.free(numberOfMarkers_data);
utils.free(geneSetSize_data);
utils.free(numberOfGenes_data);
}
return utils.toTypedArray(buffer, tmp == null, asTypedArray);
}