All files / src/math index.ts

56.45% Statements 35/62
21.42% Branches 3/14
20% Functions 2/10
52.83% Lines 28/53

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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 1292x 2x 2x 2x 2x   2x                 2x                               2x     2x                 2x     2x                                 2x         8x 2x     6x 30x   6x   6x 6x       36x 36x       139x 139x   139x 80x 7x         6x                         2x                                    
import Decimal from "decimal.js";
import { BigNumber, BigNumberish } from "ethers";
import * as ISafeNumberMath from "../ISafeNumber";
import { ISafeNumber, createSafeNumber } from "../ISafeNumber";
import { bignumberishToDecimal } from "../common";
 
export * from "./monotonic-cubic-spline";
 
export type ConvertibleToISafeNumber =
  | number
  | string
  | Decimal
  | ISafeNumber
  | BigNumber;
 
export const castToISafeNumber = (
  numberLike: ConvertibleToISafeNumber
): ISafeNumber => {
  if (typeof numberLike === "string" || typeof numberLike === "number") {
    return createSafeNumber(numberLike.toString());
  } else if (numberLike instanceof Decimal) {
    return createSafeNumber(numberLike.toString());
  } else if (numberLike instanceof BigNumber) {
    return createSafeNumber(bignumberishToDecimal(numberLike).toString());
  } else if (numberLike.isSafeNumber()) {
    return numberLike;
  } else {
    throw new Error(`Can not cast ${String(numberLike)} to ISafeNumber`);
  }
};
 
export const calculateSum = (numbers: ConvertibleToISafeNumber[]) =>
  ISafeNumberMath.calculateSum(numbers.map(castToISafeNumber)).unsafeToNumber();
 
export const calculateDeviationPercent = (args: {
  baseValue: ConvertibleToISafeNumber;
  deviatedValue: ConvertibleToISafeNumber;
}) =>
  ISafeNumberMath.calculateDeviationPercent({
    baseValue: castToISafeNumber(args.baseValue),
    deviatedValue: castToISafeNumber(args.deviatedValue),
  }).unsafeToNumber();
 
export const getMedian = (numbers: ConvertibleToISafeNumber[]) =>
  ISafeNumberMath.getMedian(numbers.map(castToISafeNumber)).unsafeToNumber();
 
export class PrecisionScaler {
  readonly tokenDecimalsScaler: Decimal;
 
  constructor(readonly tokenDecimals: number) {
    this.tokenDecimalsScaler = new Decimal(10).pow(tokenDecimals);
  }
 
  toSolidityValue(floatNumber: Decimal.Value): string {
    return new Decimal(floatNumber).mul(this.tokenDecimalsScaler).toString();
  }
 
  fromSolidityValue(contractValue: BigNumberish): Decimal {
    return bignumberishToDecimal(contractValue).div(this.tokenDecimalsScaler);
  }
}
 
/** Results are soreted ascending order */
export const filterOutliers = (
  numbers: number[],
  maxDiscrepancy: number
): { representativeGroup: number[]; outliers: number[] } => {
  // if we have only 2 values we can't set wchich is outlier which not, so we return two of them
  if (numbers.length <= 2) {
    return { representativeGroup: [...numbers], outliers: [] };
  }
 
  const sortedNumbers = [...numbers];
  sortedNumbers.sort((a, b) => a - b);
 
  let bestGroup = { startIndex: 0, endIndex: 0 };
 
  for (
    let startIndex = bestGroup.startIndex;
    startIndex < sortedNumbers.length;
    startIndex++
  ) {
    for (
      let endIndex = sortedNumbers.length;
      startIndex < endIndex;
      endIndex--
    ) {
      const firstElementValue = sortedNumbers[startIndex];
      const lastElementValue = sortedNumbers[endIndex - 1];
 
      if (lastElementValue - firstElementValue < maxDiscrepancy) {
        if (endIndex - startIndex > bestGroup.endIndex - bestGroup.startIndex)
          bestGroup = { startIndex, endIndex };
      }
    }
  }
 
  return {
    representativeGroup: sortedNumbers.slice(
      bestGroup.startIndex,
      bestGroup.endIndex
    ),
    outliers: [
      ...sortedNumbers.slice(0, bestGroup.startIndex),
      ...sortedNumbers.slice(bestGroup.endIndex, sortedNumbers.length),
    ],
  };
};
 
/** Returns -1 for empty list */
export const weightedRandom = (weights: number[]): number => {
  let totalWeight = 0;
 
  for (let i = 0; i < weights.length; i++) {
    totalWeight += weights[i];
  }
 
  let random = Math.random() * totalWeight;
 
  for (let i = 0; i < weights.length; i++) {
    random -= weights[i];
    Iif (random < 0) {
      return i;
    }
  }
 
  return weights.length - 1;
};