+ return (
+ (sortedDataSet[(sortedDataSet.length - 1) >> 1] +
+ sortedDataSet[sortedDataSet.length >> 1]) /
+ 2
+ )
+}
+
+/**
+ * Rounds the given number to the given scale.
+ * The rounding is done using the "round half away from zero" method.
+ *
+ * @param num - The number to round.
+ * @param scale - The scale to round to.
+ * @returns The rounded number.
+ * @internal
+ */
+export const round = (num: number, scale = 2): number => {
+ const rounder = Math.pow(10, scale)
+ return Math.round(num * rounder * (1 + Number.EPSILON)) / rounder
+}
+
+/**
+ * Is the given object a plain object?
+ *
+ * @param obj - The object to check.
+ * @returns `true` if the given object is a plain object, `false` otherwise.
+ * @internal
+ */
+export const isPlainObject = (obj: unknown): boolean =>
+ typeof obj === 'object' &&
+ obj !== null &&
+ obj?.constructor === Object &&
+ Object.prototype.toString.call(obj) === '[object Object]'
+
+/**
+ * Detects whether the given value is a kill behavior or not.
+ *
+ * @typeParam KB - Which specific KillBehavior type to test against.
+ * @param killBehavior - Which kind of kill behavior to detect.
+ * @param value - Any value.
+ * @returns `true` if `value` was strictly equals to `killBehavior`, otherwise `false`.
+ * @internal
+ */
+export const isKillBehavior = <KB extends KillBehavior>(
+ killBehavior: KB,
+ value: unknown
+): value is KB => {
+ return value === killBehavior
+}
+
+/**
+ * Detects whether the given value is an asynchronous function or not.
+ *
+ * @param fn - Any value.
+ * @returns `true` if `fn` was an asynchronous function, otherwise `false`.
+ * @internal
+ */
+export const isAsyncFunction = (
+ fn: unknown
+): fn is (...args: unknown[]) => Promise<unknown> => {
+ return typeof fn === 'function' && fn.constructor.name === 'AsyncFunction'
+}
+
+/**
+ * Updates the given measurement statistics.
+ *
+ * @param measurementStatistics - The measurement statistics to update.
+ * @param measurementRequirements - The measurement statistics requirements.
+ * @param measurementValue - The measurement value.
+ * @param numberOfMeasurements - The number of measurements.
+ * @internal
+ */
+export const updateMeasurementStatistics = (
+ measurementStatistics: MeasurementStatistics,
+ measurementRequirements: MeasurementStatisticsRequirements,
+ measurementValue: number
+): void => {
+ if (measurementRequirements.aggregate) {
+ measurementStatistics.aggregate =
+ (measurementStatistics.aggregate ?? 0) + measurementValue
+ measurementStatistics.minimum = min(
+ measurementValue,
+ measurementStatistics.minimum ?? Infinity
+ )
+ measurementStatistics.maximum = max(
+ measurementValue,
+ measurementStatistics.maximum ?? -Infinity
+ )
+ if (
+ (measurementRequirements.average || measurementRequirements.median) &&
+ measurementValue != null
+ ) {
+ measurementStatistics.history.push(measurementValue)
+ if (measurementRequirements.average) {
+ measurementStatistics.average = average(measurementStatistics.history)
+ } else if (measurementStatistics.average != null) {
+ delete measurementStatistics.average
+ }
+ if (measurementRequirements.median) {
+ measurementStatistics.median = median(measurementStatistics.history)
+ } else if (measurementStatistics.median != null) {
+ delete measurementStatistics.median
+ }
+ }