import * as utils from "./utils.js";
import * as wasm from "./wasm.js";
/**
* Perform a hypergeometric test, typically for over-enrichment of markers across feature sets.
* This can be computed for multiple feature 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 feature set.
* @param {number|Array|TypedArray|WasmArray} numberOfMarkers - Total number of detected markers.
* @param {number|Array|TypedArray|WasmArray} featureSetSize - Size of the feature set.
* @param {number|Array|TypedArray|WasmArray} numberOfFeatures - Total number of features.
* @param {object} [options={}] - Optional parameters.
* @param {?number} [options.numberOfThreads=null] - Number of threads to use.
* If `null`, defaults to {@linkcode maximumThreads}.
* @param {boolean} [options.assumeSorted=false] - Whether the input arrays are already sorted such that `markersInSet` is the slowest-changing value.
* Setting this to `true` can avoid an extra sorting step for greater efficiency.
*
* @return {Float64Array} 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.
*/
export function hypergeometricTest(markersInSet, numberOfMarkers, featureSetSize, numberOfFeatures, { numberOfThreads = null, assumeSorted = false } = {}) {
let markersInSet_data;
let numberOfMarkers_data;
let featureSetSize_data;
let numberOfFeatures_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 featureSetSize == "number") {
featureSetSize = [featureSetSize];
} else {
ntests = check_length(featureSetSize, "featureSetSize", ntests);
}
if (typeof numberOfFeatures == "number") {
numberOfFeatures = [numberOfFeatures];
} else {
ntests = check_length(numberOfFeatures, "numberOfFeatures", ntests);
}
if (ntests == null) {
ntests = 1;
}
let output;
let output_data;
try {
markersInSet_data = utils.wasmifyArray(markersInSet, "Int32WasmArray");
numberOfMarkers_data = utils.wasmifyArray(numberOfMarkers, "Int32WasmArray");
featureSetSize_data = utils.wasmifyArray(featureSetSize, "Int32WasmArray");
numberOfFeatures_data = utils.wasmifyArray(numberOfFeatures, "Int32WasmArray");
output_data = utils.createFloat64WasmArray(ntests);
wasm.call(module => module.hypergeometric_test(
ntests,
markersInSet_data.length != 1,
markersInSet_data.offset,
featureSetSize_data.length != 1,
featureSetSize_data.offset,
numberOfMarkers_data.length != 1,
numberOfMarkers_data.offset,
numberOfFeatures_data.length != 1,
numberOfFeatures_data.offset,
assumeSorted,
output_data.offset,
nthreads
));
output = output_data.slice();
} finally {
utils.free(markersInSet_data);
utils.free(numberOfMarkers_data);
utils.free(featureSetSize_data);
utils.free(numberOfFeatures_data);
utils.free(output_data);
}
return output;
}