{"version":3,"sources":["factorUnits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAO7C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;GAGG;AACH,qBAAa,WAAY,YAAW,cAAc,CAAC,WAAW,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAA4B;IACtE,QAAQ,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,UAAU,CAAC,CAA0B;IAC7C,OAAO,CAAC,eAAe,CAAC,CAA8B;gBAE1C,WAAW,EAAE,UAAU,EAAE,EAAE,WAAW,GAAE,OAAa;IAKjE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI;IAIxB,MAAM,CAAC,+BAA+B,CACpC,MAAM,EAAE,OAAO,EACf,GAAG,cAAc,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GACnC,WAAW;IAKd,MAAM,CAAC,gBAAgB,CAAC,GAAG,cAAc,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,WAAW;IA2B1E,MAAM,CAAC,KAAK,IAAI,WAAW;IAI3B;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM;IAOjB;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW;IAUxD;;;;OAIG;IACH,KAAK,CAAC,EAAE,EAAE,OAAO;IAIjB,kBAAkB;IAQlB,eAAe;IAOf,iBAAiB;IAOjB,cAAc,IAAI,OAAO;IAuBzB;;OAEG;IACH,6BAA6B;IAiB7B,SAAS,IAAI,WAAW;IAoBjB,MAAM,IAAI,UAAU,EAAE;WAIf,aAAa,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,EAAE;IASnE,oCAAoC,IAAI,UAAU,EAAE,EAAE;IAItD;;;;;;;;OAQG;IACH,SAAS,IAAI,WAAW;IAIxB,OAAO,CAAC,gBAAgB;IAIxB;;;;;;;;OAQG;IACI,WAAW,IAAI,WAAW;IAI1B,kBAAkB,IAAI,eAAe;IAO5C,OAAO,CAAC,sBAAsB;IAwB9B,OAAO,CAAC,kBAAkB;IAMnB,YAAY,IAAI,MAAM;IAW7B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAI/B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAOlC,OAAO,CAAC,MAAM,CAAC,SAAS;IAkBxB,OAAO,CAAC,6BAA6B;IAQ9B,iCAAiC,IAAI,MAAM,EAAE;IAUpD,MAAM,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO;IAcpC,QAAQ,IAAI,MAAM;CAYnB","file":"factorUnits.d.ts","sourcesContent":["import { SupportsEquals } from \"./baseTypes.js\";\nimport { FactorUnit } from \"./factorUnit.js\";\nimport {\n  arrayEqualsIgnoreOrdering,\n  compareUsingEquals,\n  isNullish,\n  ONE,\n} from \"./utils.js\";\nimport { Unit } from \"./unit.js\";\nimport { Decimal } from \"decimal.js\";\nimport { DimensionVector } from \"./dimensionVector.js\";\n\n/**\n * Class representing a set of FactorUnits and a conversionMultiplier, so the units can be\n * used to replace any other unit of the same dimensionality.\n */\nexport class FactorUnits implements SupportsEquals<FactorUnits> {\n  private static readonly EMPTY_FACTOR_UNITS = new FactorUnits([], ONE);\n  readonly factorUnits: FactorUnit[];\n  readonly scaleFactor: Decimal;\n  private normalized?: FactorUnits = undefined;\n  private dimensionVector?: DimensionVector = undefined;\n\n  constructor(factorUnits: FactorUnit[], scaleFactor: Decimal = ONE) {\n    this.factorUnits = factorUnits;\n    this.scaleFactor = scaleFactor;\n  }\n\n  static ofUnit(unit: Unit) {\n    return new FactorUnits([FactorUnit.ofUnit(unit)]);\n  }\n\n  static ofFactorUnitSpecWithScaleFactor(\n    scalar: Decimal,\n    ...factorUnitSpec: (Unit | number)[]\n  ): FactorUnits {\n    const withoutScalar = this.ofFactorUnitSpec(...factorUnitSpec);\n    return new FactorUnits(withoutScalar.factorUnits, scalar);\n  }\n\n  static ofFactorUnitSpec(...factorUnitSpec: (Unit | number)[]): FactorUnits {\n    if (factorUnitSpec.length % 2 !== 0) {\n      throw \"An even number of arguments is required\";\n    }\n    if (factorUnitSpec.length > 14) {\n      throw \"No more than 14 arguments (7 factor units) are supported\";\n    }\n    const factorUnits = [];\n    for (let i = 0; i < factorUnitSpec.length; i += 2) {\n      const requestedUnit = factorUnitSpec[i];\n      const requestedExponent = factorUnitSpec[i + 1];\n      if (!(requestedUnit instanceof Unit)) {\n        throw `argument at 0-based position ${i} is not of type Unit. The input must be between 1 and 7 Unit, exponent pairs`;\n      }\n      if (\n        typeof requestedExponent !== \"number\" ||\n        !Number.isInteger(requestedExponent)\n      ) {\n        throw `argument at 0-based position ${\n          i + 1\n        } is not of type number or not an integer. The input must be between 1 and 7 Unit, exponent pairs`;\n      }\n      factorUnits.push(new FactorUnit(requestedUnit, requestedExponent));\n    }\n    return new FactorUnits(factorUnits);\n  }\n\n  static empty(): FactorUnits {\n    return FactorUnits.EMPTY_FACTOR_UNITS;\n  }\n\n  /**\n   * Returns this ScaledFactorUnits object, raised to the specified power.\n   * @param power\n   */\n  pow(power: number) {\n    return new FactorUnits(\n      this.factorUnits.map((fu) => fu.pow(power)),\n      this.scaleFactor.pow(power)\n    );\n  }\n\n  /**\n   *\n   * @param other\n   */\n  combineWith(other: FactorUnits | undefined): FactorUnits {\n    if (!other) {\n      return this;\n    }\n    return new FactorUnits(\n      FactorUnit.contractExponents([...this.factorUnits, ...other.factorUnits]),\n      this.scaleFactor.mul(other.scaleFactor)\n    );\n  }\n\n  /**\n   * Returns this ScaledFactorUnits object, with its conversionMultiplier multiplied by the specified value.\n   *\n   * @param by\n   */\n  scale(by: Decimal) {\n    return new FactorUnits(this.factorUnits, this.scaleFactor.mul(by));\n  }\n\n  isRatioOfSameUnits() {\n    return (\n      this.factorUnits.length === 2 &&\n      this.factorUnits[0].unit.equals(this.factorUnits[1].unit) &&\n      this.factorUnits[0].exponent === this.factorUnits[1].exponent * -1\n    );\n  }\n\n  reduceExponents() {\n    return new FactorUnits(\n      FactorUnit.reduceExponents(this.factorUnits),\n      this.scaleFactor\n    );\n  }\n\n  contractExponents() {\n    return new FactorUnits(\n      FactorUnit.contractExponents(this.factorUnits),\n      this.scaleFactor\n    );\n  }\n\n  hasFactorUnits(): boolean {\n    if (isNullish(this.factorUnits)) {\n      return false;\n    }\n    if (this.factorUnits.length === 0) {\n      return false;\n    }\n    if (\n      this.factorUnits.length === 1 &&\n      !this.factorUnits[0].unit.factorUnits.equals(this)\n    ) {\n      return true;\n    }\n    if (\n      this.factorUnits.length === 1 &&\n      this.factorUnits[0].exponent === 1 &&\n      ONE.equals(this.scaleFactor)\n    ) {\n      return false;\n    }\n    return true;\n  }\n\n  /**\n   * Returns true iff this factorUnits object has exactly one factor unit, which has exponent 1.\n   */\n  isOneOtherUnitWithExponentOne() {\n    if (isNullish(this.factorUnits)) {\n      return false;\n    }\n    if (this.factorUnits.length !== 1) {\n      return false;\n    }\n    const factorUnit = this.factorUnits[0];\n    if (factorUnit.exponent !== 1) {\n      return false;\n    }\n    if (factorUnit.unit.factorUnits.equals(this)) {\n      return false;\n    }\n    return true;\n  }\n\n  normalize(): FactorUnits {\n    if (!isNullish(this.normalized)) {\n      return this.normalized as FactorUnits;\n    }\n    let normalized = null;\n    if (this.hasFactorUnits()) {\n      const mapped = this.factorUnits.map((fu) =>\n        fu.unit.normalize().pow(fu.exponent)\n      );\n      normalized = mapped.reduce((prev, cur) => prev.combineWith(cur));\n    } else {\n      normalized = new FactorUnits(this.factorUnits, this.scaleFactor);\n    }\n    if (!normalized.isRatioOfSameUnits()) {\n      normalized = normalized.reduceExponents();\n    }\n    this.normalized = normalized.scale(this.scaleFactor);\n    return this.normalized;\n  }\n\n  public expand(): FactorUnit[] {\n    return FactorUnits.expandFactors(this);\n  }\n\n  public static expandFactors(factorUnits: FactorUnits): FactorUnit[] {\n    if (!factorUnits.hasFactorUnits()) {\n      return [...factorUnits.factorUnits];\n    }\n    return factorUnits.factorUnits.flatMap((fu) =>\n      FactorUnits.expandFactors(fu.unit.factorUnits)\n    );\n  }\n\n  getAllPossibleFactorUnitCombinations(): FactorUnit[][] {\n    return FactorUnit.getAllPossibleFactorUnitCombinations(this.factorUnits);\n  }\n\n  /**\n   * Returns a FactorUnits object containing the scaleFactor and the factors in the numerator of\n   * this FactorUnits object. Note that any derived units in the numerator are returned without\n   * recursive decomposition. For example, for `5.0 * M2-PER-N` a FactorUnit object representing\n   * `N` is returned.\n   *\n   * @return a FactorUnits object representing the scaleFactor and the numerator units of this\n   *     unit.\n   */\n  numerator(): FactorUnits {\n    return new FactorUnits(this.numeratorFactors(), this.scaleFactor);\n  }\n\n  private numeratorFactors(): FactorUnit[] {\n    return this.factorUnits.filter((fu) => fu.exponent > 0);\n  }\n\n  /**\n   * Returns a FactorUnits object containing the factors in the denominator of this FactorUnits\n   * object. Note that any derived units in the denominator are returned without recursive\n   * decomposition. For example, for `5.0 * ` a FactorUnit object representing `5.0 * N` is\n   * returned.\n   *\n   * @return a FactorUnits object representing the scaleFactor and the numerator units of this\n   *     unit.\n   */\n  public denominator(): FactorUnits {\n    return new FactorUnits(this.denominatorFactors());\n  }\n\n  public getDimensionVector(): DimensionVector {\n    if (isNullish(this.dimensionVector)) {\n      this.dimensionVector = this.computeDimensionVector();\n    }\n    return this.dimensionVector as DimensionVector;\n  }\n\n  private computeDimensionVector(): DimensionVector {\n    if (isNullish(this.factorUnits) || this.factorUnits.length == 0) {\n      return DimensionVector.DIMENSIONLESS;\n    }\n\n    let dv: DimensionVector | undefined = undefined;\n    for (const fu of this.factorUnits) {\n      const factorUnitFeatureVector = fu.getDimensionVector();\n      if (isNullish(factorUnitFeatureVector)) {\n        throw new Error(\n          `Cannot compute dimension vector of factor units ${this.toString()}: ${fu.unit.getIriAbbreviated()} does not have a dimension vector`\n        );\n      }\n      if (isNullish(dv)) {\n        dv = factorUnitFeatureVector;\n      } else {\n        dv = (dv as DimensionVector).combine(\n          factorUnitFeatureVector as DimensionVector\n        );\n      }\n    }\n    return dv as DimensionVector;\n  }\n\n  private denominatorFactors(): FactorUnit[] {\n    return this.factorUnits\n      .filter((fu) => fu.exponent < 0)\n      .map((fu) => fu.pow(-1));\n  }\n\n  public getLocalname(): string {\n    return this.generateLocalname(\n      this.numeratorFactors()\n        .map((fu) => FactorUnits.factorUnitLocalname(fu))\n        .join(\"-\"),\n      this.denominatorFactors()\n        .map((fu) => FactorUnits.factorUnitLocalname(fu))\n        .join(\"-\")\n    );\n  }\n\n  private static isEmptyOrNullish(val?: string): boolean {\n    return val === null || typeof val === \"undefined\" || val.length == 0;\n  }\n\n  private generateLocalname(numerator = \"\", denominator = \"\"): string {\n    let completeString = numerator;\n    if (!FactorUnits.isEmptyOrNullish(denominator)) {\n      if (!FactorUnits.isEmptyOrNullish(numerator)) {\n        completeString += \"-\";\n      }\n      completeString += \"PER-\" + denominator;\n    }\n    return completeString;\n  }\n\n  private static factorUnitLocalname(fu: FactorUnit): string {\n    return (\n      fu.unit.getIriLocalname() +\n      (Math.abs(fu.exponent) > 1 ? Math.abs(fu.exponent) : \"\")\n    );\n  }\n\n  private static permutate(strings: string[]): string[][] {\n    const ret = [];\n    if (strings.length <= 1) {\n      ret.push(strings);\n      return ret;\n    }\n    for (let i = 0; i < strings.length; i++) {\n      const otherElements = [...strings];\n      otherElements.splice(i, 1);\n      const othersPermutated = this.permutate(otherElements);\n      for (const otherPermutated of othersPermutated) {\n        otherPermutated.unshift(strings[i]);\n      }\n      ret.push(...othersPermutated);\n    }\n    return ret;\n  }\n\n  private permutateFactorUnitLocalnames(\n    predicate: (fu: FactorUnit) => boolean\n  ): string[] {\n    return FactorUnits.permutate(\n      this.factorUnits.filter(predicate).map(FactorUnits.factorUnitLocalname)\n    ).map((strings) => strings.join(\"-\"));\n  }\n\n  public generateAllLocalnamePossibilities(): string[] {\n    return this.permutateFactorUnitLocalnames((fu) => fu.exponent > 0).flatMap(\n      (numeratorString) =>\n        this.permutateFactorUnitLocalnames((fu) => fu.exponent < 0).map(\n          (denominatorString) =>\n            this.generateLocalname(numeratorString, denominatorString)\n        )\n    );\n  }\n\n  equals(other?: FactorUnits): boolean {\n    if (!other) {\n      return false;\n    }\n    return (\n      this.scaleFactor.eq(other?.scaleFactor) &&\n      arrayEqualsIgnoreOrdering(\n        this.factorUnits,\n        other?.factorUnits,\n        compareUsingEquals\n      )\n    );\n  }\n\n  toString(): string {\n    return (\n      (this.scaleFactor.eq(ONE) ? \"\" : this.scaleFactor.toString() + \"*\") +\n      (this.factorUnits.length > 0\n        ? \"[\" +\n          this.factorUnits\n            .map((s) => s.toString())\n            .reduce((p, n) => p + \", \" + n) +\n          \"]\"\n        : \"[no factors]\")\n    );\n  }\n}\n"]}