All files / src/ISafeNumber ISafeNumberWeighted.ts

83.87% Statements 26/31
100% Branches 6/6
66.66% Functions 6/9
86.36% Lines 19/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 586x             6x     3x 1x     2x 7x     9x       2x 2x 6x 6x 6x 1x 5x 1x                 6x     6x 6x           6x                  
import { ISafeNumber, SafeZero, calculateSum, getMedian } from ".";
 
export type WeightedValue = {
  value: ISafeNumber;
  weight: ISafeNumber;
};
 
export const getWeightedMedian = (
  weightedValues: WeightedValue[]
): ISafeNumber => {
  if (weightedValues.length === 0) {
    throw new Error("Cannot get weighted median value of an empty array");
  }
 
  const sortedValues: WeightedValue[] = [...weightedValues].sort((a, b) =>
    a.value.lt(b.value) ? -1 : 1
  );
 
  const halfTotalWeight = calculateSum(sortedValues.map((wv) => wv.weight)).div(
    2
  );
 
  let cumulativeWeight = SafeZero;
  for (let i = 0; i < sortedValues.length; i++) {
    const weightedValue = sortedValues[i];
    cumulativeWeight = cumulativeWeight.add(weightedValue.weight);
    if (cumulativeWeight.gt(halfTotalWeight)) {
      return weightedValue.value;
    } else if (cumulativeWeight.eq(halfTotalWeight)) {
      return weightedValue.value.add(sortedValues[i + 1].value).div(2);
    }
  }
 
  throw new Error(
    "This should not happen: the accumulated weight did not exceed half of the sum of the weights"
  );
};
 
export const logarithmicWeighting = (
  weightedValues: WeightedValue[]
): WeightedValue[] => {
  const medianWeight = getMedian(weightedValues.map((v) => v.weight));
  return weightedValues.map((weightedValue) => ({
    value: weightedValue.value,
    weight: weightedValue.weight.div(medianWeight).add(1).log2(),
  }));
};
 
export const normalizeWeightedValues = (
  weightedValues: WeightedValue[]
): WeightedValue[] => {
  const sum = calculateSum(weightedValues.map((value) => value.weight));
  return weightedValues.map((weightedValue) => ({
    value: weightedValue.value,
    weight: weightedValue.weight.div(sum),
  }));
};