{"version":3,"sources":["qudt.ts"],"names":[],"mappings":"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","file":"qudt.d.ts","sourcesContent":["import { Unit } from \"./unit.js\";\nimport {\n  arrayContains,\n  arrayDeduplicate,\n  arrayMax,\n  arrayMin,\n  BooleanComparator,\n  compareUsingEquals,\n  findInIterable,\n  getLastIriElement,\n  NumberComparator,\n  StringComparator,\n  OrderComparator,\n  ONE,\n  ZERO,\n  isNullish,\n} from \"./utils.js\";\nimport { QuantityKind } from \"./quantityKind.js\";\nimport { Prefix } from \"./prefix.js\";\nimport { DerivedUnitSearchMode } from \"./derivedUnitSearchMode.js\";\nimport { FactorUnit } from \"./factorUnit.js\";\nimport { FactorUnits } from \"./factorUnits.js\";\nimport { AssignmentProblem } from \"./assignmentProblem.js\";\nimport { QuantityValue } from \"./quantityValue.js\";\nimport { LangString } from \"./langString.js\";\nimport { Decimal } from \"decimal.js\";\nimport { QudtNamespaces } from \"./qudtNamespaces.js\";\nimport { SystemOfUnits } from \"./systemOfUnits.js\";\nimport { DimensionVector } from \"./dimensionVector.js\";\n\nexport class QudtlibConfig {\n  readonly units: Map<string, Unit>;\n  readonly quantityKinds: Map<string, QuantityKind>;\n  readonly prefixes: Map<string, Prefix>;\n  readonly systemsOfUnits: Map<string, SystemOfUnits>;\n  readonly unitsByDimensionVector: Map<string, Array<Unit>>;\n\n  constructor() {\n    this.units = new Map<string, Unit>();\n    this.quantityKinds = new Map<string, QuantityKind>();\n    this.prefixes = new Map<string, Prefix>();\n    this.systemsOfUnits = new Map<string, SystemOfUnits>();\n    this.unitsByDimensionVector = new Map<string, Array<Unit>>();\n  }\n\n  public indexUnitsByDimensionVector() {\n    this.units.forEach((u) => {\n      const dimVector = u.dimensionVectorIri;\n      if (!isNullish(dimVector)) {\n        let unitsWithSameDimVector = this.unitsByDimensionVector.get(\n          dimVector as string\n        );\n        if (isNullish(unitsWithSameDimVector)) {\n          unitsWithSameDimVector = [];\n          this.unitsByDimensionVector.set(\n            dimVector as string,\n            unitsWithSameDimVector\n          );\n        }\n        (unitsWithSameDimVector as Array<Unit>).push(u);\n      }\n    });\n  }\n}\n\nexport const config = new QudtlibConfig();\nexport class Qudt {\n  public static NAMESPACES = QudtNamespaces;\n\n  /**\n   * Returns the {@link Unit} identified the specified IRI. For example, <code>\n   * unit(\"http://qudt.org/vocab/unit/N-PER-M2\")</code> yields `Units.N__PER__M2`,\n   * if that unit has been loaded;\n   *\n   * @param iri the requested unit IRI\n   * @return the unit or `undefined` if no unit is found\n   */\n  static unit(unitIri: string): Unit | undefined {\n    return config.units.get(unitIri);\n  }\n\n  /**\n   * Same as {@link #unit(string)} but throws an exception if no unit is found.\n   * @param unitIri the unit IRI\n   * @return the unit\n   */\n  static unitRequired(unitIri: string): Unit {\n    const ret = Qudt.unit(unitIri);\n    if (typeof ret === \"undefined\") {\n      throw `Unit ${unitIri} not found`;\n    }\n    return ret;\n  }\n  /**\n   * Returns the first unit found whose label matches the specified label after replacing any\n   * underscore with space and ignoring case (US locale). If more intricate matching is needed,\n   * clients can use `{@link #allUnits()}.filter(...)`.\n   *\n   * @param label the matched label\n   * @return the first unit found\n   */\n  static unitFromLabel(label: string): Unit | undefined {\n    const matcher: LabelMatcher =\n      new CaseInsensitiveUnderscoreIgnoringLabelMatcher(label);\n    const firstMatch: Unit | undefined = findInIterable(\n      config.units.values(),\n      (u) =>\n        matcher.matchesLangStrings(u.labels) ||\n        (!!u.currencyCode && matcher.matchesString(u.currencyCode))\n    );\n    return firstMatch;\n  }\n\n  static unitFromLabelRequired(label: string): Unit {\n    const match = this.unitFromLabel(label);\n    if (!match) throw `No unit found for label ${label}`;\n    return match;\n  }\n\n  static unitFromLocalname(localname: string): Unit | undefined {\n    return Qudt.unit(Qudt.unitIriFromLocalname(localname));\n  }\n\n  static unitFromLocalnameRequired(localname: string): Unit {\n    return Qudt.unitRequired(Qudt.unitIriFromLocalname(localname));\n  }\n\n  static unitIriFromLocalname(localname: string): string {\n    return Qudt.NAMESPACES.unit.makeIriInNamespace(localname);\n  }\n\n  static currencyFromLocalname(localname: string): Unit | undefined {\n    return Qudt.unit(Qudt.currencyIriFromLocalname(localname));\n  }\n\n  static currencyFromLocalnameRequired(localname: string): Unit {\n    return Qudt.unitRequired(Qudt.currencyIriFromLocalname(localname));\n  }\n\n  static currencyIriFromLocalname(localname: string): string {\n    return Qudt.NAMESPACES.currency.makeIriInNamespace(localname);\n  }\n\n  static quantityKind(quantityKindIri: string): QuantityKind | undefined {\n    return config.quantityKinds.get(quantityKindIri);\n  }\n\n  static quantityKindRequired(quantityKindIri: string): QuantityKind {\n    const ret = Qudt.quantityKind(quantityKindIri);\n    if (typeof ret === \"undefined\") {\n      throw `QuantityKind ${quantityKindIri} not found`;\n    }\n    return ret;\n  }\n\n  static quantityKindFromLocalname(\n    localname: string\n  ): QuantityKind | undefined {\n    return Qudt.quantityKind(Qudt.quantityKindIriFromLocalname(localname));\n  }\n\n  static quantityKindFromLocalnameRequired(localname: string): QuantityKind {\n    return Qudt.quantityKindRequired(\n      Qudt.quantityKindIriFromLocalname(localname)\n    );\n  }\n\n  static quantityKindIriFromLocalname(localname: string): string {\n    return Qudt.NAMESPACES.quantityKind.makeIriInNamespace(localname);\n  }\n\n  static quantityKinds(unit: Unit): QuantityKind[] {\n    return unit.quantityKindIris.map((iri) => Qudt.quantityKindRequired(iri));\n  }\n\n  static quantityKindsBroad(unit: Unit): QuantityKind[] {\n    let current: QuantityKind[] = Qudt.quantityKinds(unit);\n    const result: QuantityKind[] = [];\n    current.forEach((qk) => result.push(qk));\n    while (current.length) {\n      current = current\n        .flatMap((qk) => qk.broaderQuantityKindIris)\n        .map((iri) => Qudt.quantityKindRequired(iri));\n      current.forEach((qk) => result.includes(qk) || result.push(qk));\n    }\n    return result;\n  }\n\n  static isBroaderQuantityKind(\n    suspectedBroader: QuantityKind,\n    quantityKind: QuantityKind\n  ): boolean {\n    const broader = quantityKind.broaderQuantityKindIris;\n    if (broader.length === 0) {\n      return false;\n    }\n    if (broader.includes(suspectedBroader.iri)) {\n      return true;\n    }\n    return broader.some((b) =>\n      Qudt.isBroaderQuantityKind(Qudt.quantityKindRequired(b), suspectedBroader)\n    );\n  }\n\n  static prefixFromLabelRequired(label: string): Prefix {\n    const match = this.prefixFromLabel(label);\n    if (!match) throw `No prefix found for label ${label}`;\n    return match;\n  }\n\n  static prefixFromLabel(label: string): Prefix | undefined {\n    const matcher: LabelMatcher =\n      new CaseInsensitiveUnderscoreIgnoringLabelMatcher(label);\n    const firstMatch: Prefix | undefined = findInIterable(\n      config.prefixes.values(),\n      (u) => matcher.matchesLangStrings(u.labels)\n    );\n    return firstMatch;\n  }\n\n  static prefixFromLocalname(localname: string): Prefix | undefined {\n    return Qudt.prefix(Qudt.prefixIriFromLocalname(localname));\n  }\n\n  static prefixFromLocalnameRequired(localname: string): Prefix {\n    return Qudt.prefixRequired(Qudt.prefixIriFromLocalname(localname));\n  }\n\n  static prefixIriFromLocalname(localname: string): string {\n    return Qudt.NAMESPACES.prefix.makeIriInNamespace(localname);\n  }\n\n  static prefix(prefixIri: string): Prefix | undefined {\n    return config.prefixes.get(prefixIri);\n  }\n\n  static prefixRequired(prefixIri: string): Prefix {\n    const ret = Qudt.prefix(prefixIri);\n    if (typeof ret === \"undefined\") {\n      throw `Prefix ${prefixIri} not found`;\n    }\n    return ret;\n  }\n\n  static systemOfUnitsFromLabelRequired(label: string): SystemOfUnits {\n    const match = this.systemOfUnitsFromLabel(label);\n    if (!match) throw `No systemOfUnits found for label ${label}`;\n    return match;\n  }\n\n  static systemOfUnitsFromLabel(label: string): SystemOfUnits | undefined {\n    const matcher: LabelMatcher =\n      new CaseInsensitiveUnderscoreIgnoringLabelMatcher(label);\n    const firstMatch: SystemOfUnits | undefined = findInIterable(\n      config.systemsOfUnits.values(),\n      (u) => matcher.matchesLangStrings(u.labels)\n    );\n    return firstMatch;\n  }\n\n  static systemOfUnitsFromLocalname(\n    localname: string\n  ): SystemOfUnits | undefined {\n    return Qudt.systemOfUnits(Qudt.systemOfUnitsIriFromLocalname(localname));\n  }\n\n  static systemOfUnitsFromLocalnameRequired(localname: string): SystemOfUnits {\n    return Qudt.systemOfUnitsRequired(\n      Qudt.systemOfUnitsIriFromLocalname(localname)\n    );\n  }\n\n  static systemOfUnitsIriFromLocalname(localname: string): string {\n    return Qudt.NAMESPACES.systemOfUnits.makeIriInNamespace(localname);\n  }\n\n  static systemOfUnits(systemOfUnitsIri: string): SystemOfUnits | undefined {\n    return config.systemsOfUnits.get(systemOfUnitsIri);\n  }\n\n  static systemOfUnitsRequired(systemOfUnitsIri: string): SystemOfUnits {\n    const ret = Qudt.systemOfUnits(systemOfUnitsIri);\n    if (typeof ret === \"undefined\") {\n      throw `SystemOfUnits ${systemOfUnitsIri} not found`;\n    }\n    return ret;\n  }\n\n  /**\n   * Obtains units based on factor units, using the specified {@link DerivedUnitSearchMode}.\n   *\n   * For example,\n   *\n   * ```\n   *   const spec = new Map<Unit, number>();\n   *    spec.set(Units.M, 1);\n   *    spec.set(Units.KiloGM, 1);\n   *    spec.set(Units.SEC, -2);\n   * Qudt.derivedUnitsFrom Map(\n   *    DerivedUnitSearchMode.BEST_MATCH, spec);\n   * ```\n   *\n   * will yield an array containing the Newton Unit ({@code Qudt.Units.N})\n   *\n   * @param searchMode the {@link DerivedUnitSearchMode} to use\n   * @param factorUnits a map containing unit to exponent entries.\n   * @return the derived units that match the given factor units\n   */\n  static derivedUnitsFromMap(\n    searchMode: DerivedUnitSearchMode,\n    factorUnits: Map<Unit, number>\n  ): Unit[] {\n    const flattened: (Unit | number)[] = [];\n    for (const [key, value] of factorUnits.entries()) {\n      flattened.push(key, value);\n    }\n    return this.derivedUnitsFromExponentUnitPairs(searchMode, ...flattened);\n  }\n\n  /**\n   * Obtains units based on factor units.\n   *\n   * @param searchMode the {@link DerivedUnitSearchMode} to use\n   * @param factorUnits the factor units\n   * @return the derived unit that match the given factor units\n   * @see #derivedUnitsFromMap(DerivedUnitSearchMode, Map)\n   */\n  static derivedUnitsFromFactorUnits(\n    searchMode: DerivedUnitSearchMode,\n    ...factorUnits: FactorUnit[]\n  ): Unit[] {\n    return this.derivedUnitsFromFactorUnitSelection(\n      searchMode,\n      new FactorUnits(factorUnits)\n    );\n  }\n\n  /**\n   * Vararg method, must be an even number of arguments, always alternating types of Unit|String\n   * and Integer.\n   *\n   * @param factorUnitSpec alternating Unit (representing a unit IRI) and Decimal|number (the\n   *     exponent)\n   * @return the units that match\n   */\n  static derivedUnitsFromExponentUnitPairs(\n    searchMode: DerivedUnitSearchMode,\n    ...factorUnitSpecs: (Unit | string | number)[]\n  ): Unit[] {\n    const spec: (Unit | number)[] = [];\n    for (let i = 0; i < factorUnitSpecs.length; i++) {\n      const specAtI: Unit | string | number = factorUnitSpecs[i];\n      if (i % 2 == 0 && specAtI instanceof Unit) {\n        spec[i] = specAtI as Unit;\n      } else if (i % 2 == 0 && typeof specAtI === \"string\") {\n        const unitString: string = specAtI as string;\n        let unit = Qudt.unit(unitString);\n        if (typeof unit === \"undefined\") {\n          unit = Qudt.unitFromLocalname(unitString);\n        }\n        if (typeof unit === \"undefined\") {\n          unit = Qudt.unitFromLabel(unitString);\n        }\n        if (typeof unit === \"undefined\") {\n          throw `Unable to find unit for string ${unitString}, interpreted as iri, label or localname`;\n        }\n        spec[i] = unit;\n      } else if (i % 2 == 1 && typeof specAtI === \"number\") {\n        spec[i] = specAtI;\n      } else {\n        throw `Cannot handle input ${specAtI} at 0-based position ${i}`;\n      }\n    }\n    const initialFactorUnitSelection = FactorUnits.ofFactorUnitSpec(...spec);\n    return Qudt.derivedUnitsFromFactorUnitSelection(\n      searchMode,\n      initialFactorUnitSelection\n    );\n  }\n\n  /**\n   * @param searchMode the {@link DerivedUnitSearchMode} to use\n   * @param selection the factor unit selection\n   * @return the units that match\n   * @see #derivedUnitsFromMap(DerivedUnitSearchMode, Map)\n   */\n  static derivedUnitsFromFactorUnitSelection(\n    searchMode: DerivedUnitSearchMode,\n    selection: FactorUnits\n  ): Unit[] {\n    const matchingUnits = this.findMatchingUnits(selection);\n    if (searchMode === DerivedUnitSearchMode.ALL || matchingUnits.length < 2) {\n      return matchingUnits.sort(\n        Qudt.bestMatchForFactorUnitsComparator(selection)\n      );\n    } else {\n      const requestedUnits: FactorUnit[] =\n        selection.contractExponents().factorUnits;\n      return [\n        arrayMin(\n          matchingUnits,\n          Qudt.bestMatchForFactorUnitsComparator(selection)\n        ),\n      ];\n    }\n  }\n\n  private static bestMatchForFactorUnitsComparator(\n    requestedFactorUnits: FactorUnits\n  ): OrderComparator<Unit> {\n    const reqNorm = requestedFactorUnits.normalize();\n    const reqNum = requestedFactorUnits.numerator();\n    const reqNumNorm = reqNum.normalize();\n    const reqDen = requestedFactorUnits.denominator();\n    const reqDenNorm = reqDen.normalize();\n    const reqLocalNamePossibilities: string[] =\n      requestedFactorUnits.generateAllLocalnamePossibilities();\n\n    return (left: Unit, right: Unit) => {\n      if (left.factorUnits.equals(requestedFactorUnits)) {\n        if (!right.factorUnits.equals(requestedFactorUnits)) {\n          return -1;\n        }\n      } else {\n        if (right.factorUnits.equals(requestedFactorUnits)) {\n          return 1;\n        }\n      }\n      if (left.getIriLocalname().indexOf(\"-\") === -1) {\n        if (right.getIriLocalname().indexOf(\"-\") > -1) {\n          return -1; // prefer a derived unit with a new name (such as W, J, N etc.)\n        }\n      } else if (right.getIriLocalname().indexOf(\"-\") === -1) {\n        return 1;\n      }\n\n      const leftDen: FactorUnits = left.factorUnits.denominator();\n      const rightDen: FactorUnits = right.factorUnits.denominator();\n      const leftFactorsDenCnt: number = leftDen.expand().length;\n      const rightFactorsDenCnt: number = rightDen.expand().length;\n      const reqFactorsDenCnt: number = reqDen.expand().length;\n      const diffFactorsCountDen =\n        Math.abs(reqFactorsDenCnt - leftFactorsDenCnt) -\n        Math.abs(reqFactorsDenCnt - rightFactorsDenCnt);\n      if (diffFactorsCountDen != 0) {\n        return diffFactorsCountDen;\n      }\n\n      const leftNum: FactorUnits = left.factorUnits.numerator();\n      const rightNum: FactorUnits = right.factorUnits.denominator();\n      const leftFactorsNumCnt: number = leftNum.expand().length;\n      const rightFactorsNumCnt: number = rightNum.expand().length;\n      const reqFactorsNumCnt: number = reqNum.expand().length;\n      const diffFactorsCountNum: number =\n        Math.abs(reqFactorsNumCnt - leftFactorsNumCnt) -\n        Math.abs(reqFactorsNumCnt - rightFactorsNumCnt);\n      if (diffFactorsCountNum != 0) {\n        return diffFactorsCountNum;\n      }\n      const leftCnt: number = left.factorUnits.expand().length;\n      const rightCnt: number = right.factorUnits.expand().length;\n      const reqCnt: number = requestedFactorUnits.expand().length;\n      if (leftCnt == reqCnt) {\n        if (rightCnt != reqCnt) {\n          return -1;\n        }\n      } else {\n        if (rightCnt == reqCnt) {\n          return 1;\n        }\n      }\n      if (reqLocalNamePossibilities.includes(left.getIriLocalname())) {\n        if (!reqLocalNamePossibilities.includes(right.getIriLocalname())) {\n          return -1;\n        }\n      } else if (reqLocalNamePossibilities.includes(right.getIriLocalname())) {\n        return 1;\n      }\n      return StringComparator(left.getIriLocalname(), right.getIriLocalname());\n    };\n  }\n\n  private static findMatchingUnits(\n    initialFactorUnitSelection: FactorUnits\n  ): Unit[] {\n    const matchingUnits: Unit[] = [];\n    const unitsWithSameDimVector = this.getUnitsByDimensionVector(\n      initialFactorUnitSelection.getDimensionVector()\n    );\n    if (\n      isNullish(unitsWithSameDimVector) ||\n      (unitsWithSameDimVector as Array<Unit>).length == 0\n    ) {\n      return [];\n    }\n    for (const unit of unitsWithSameDimVector as Array<Unit>) {\n      if (unit.matches(initialFactorUnitSelection)) {\n        matchingUnits.push(unit);\n      }\n    }\n    arrayDeduplicate(matchingUnits, compareUsingEquals);\n    return matchingUnits;\n  }\n\n  public static getUnitsByDimensionVector(\n    dimVector: DimensionVector | string\n  ): Array<Unit> {\n    let dimVectorIri: string | undefined = undefined;\n    if (typeof dimVector === \"string\") {\n      dimVectorIri = dimVector;\n    } else {\n      dimVectorIri = dimVector.getDimensionVectorIri();\n    }\n    const unitsWithSameDimVector =\n      config.unitsByDimensionVector.get(dimVectorIri);\n    if (isNullish(unitsWithSameDimVector)) {\n      return [];\n    }\n    return unitsWithSameDimVector as Array<Unit>;\n  }\n\n  public static getUnitsWithSameDimensionVector(unit: Unit) {\n    if (isNullish(unit.dimensionVectorIri)) {\n      throw new Error(\n        `unit ${unit.getIriAbbreviated()} does not have a dimension vector iri`\n      );\n    }\n    return this.getUnitsByDimensionVector(unit.dimensionVectorIri as string);\n  }\n\n  /**\n   * Returns the unit resulting from scaling the specified `unit` with the specified `prefix`.\n   * NOTE: if you have unit/prefix labels (not IRIs) - such as \"KILO\", \"NEWTON\", use {@link #scaleUnitFromLabels(string, string):Unit}.\n   *\n   * @param prefix the prefix to use for scaling or its IRI.\n   * @param baseUnit the unit to scale or its IRI\n   * @return the resulting unit\n   * @throws exception if no such unit is found\n   */\n  static scale(prefix: Prefix | string, baseUnit: Unit | string) {\n    const thePrefix =\n      prefix instanceof Prefix ? prefix : this.prefixRequired(prefix);\n    const theUnit =\n      baseUnit instanceof Unit ? baseUnit : this.unitRequired(baseUnit);\n    for (const u of this.getUnitsWithSameDimensionVector(theUnit)) {\n      if (u.prefix?.equals(thePrefix) && u.scalingOf?.equals(theUnit)) {\n        return u;\n      }\n    }\n    throw `No scaled unit found with base unit ${theUnit.toString()} and prefix ${thePrefix.toString()}`;\n  }\n\n  /**\n   * Returns the unit resulting from scaling the specified `baseUnitLabel` label\n   * (such as \"METER\") with the specified `prefixLabel` (such as \"KILO\").\n   *\n   * @param prefixLabel the label of the prefix, case-insensitive , such as \"KILO\", or \"kilo\"\n   * @param baseUnitLabel the label of the base unit, case-insensitive, such as \"Meter\"\n   */\n  static scaleUnitFromLabels(prefixLabel: string, baseUnitLabel: string) {\n    return this.scale(\n      Qudt.prefixFromLabelRequired(prefixLabel),\n      Qudt.unitFromLabelRequired(baseUnitLabel)\n    );\n  }\n\n  /**\n   * Returns the list of {@link FactorUnit}s of the specified `unit`.\n   *\n   * @param unit the unit to get factors for\n   * @return the factors of the unit or an empty list if the unit is not a derived unit\n   */\n  static factorUnits(unit: Unit): FactorUnit[] {\n    return FactorUnit.contractExponents(\n      unit.getLeafFactorUnitsWithCumulativeExponents()\n    );\n  }\n\n  /**\n   * Perform mathematical simplification on factor units. Only simplifies units with exponents of the same sign.\n   *\n   * For example,\n   * ```\n   * N / M * M -> N per M^2\n   * ```\n   *\n   * @param factorUnits the factor units to simplify\n   * @return the simplified factor units.\n   * @deprecated `use contractExponents(FactorUnit[]): FactorUnit[]` instead\n   */\n  static simplifyFactorUnits(factorUnits: FactorUnit[]): FactorUnit[] {\n    return FactorUnit.contractExponents(factorUnits);\n  }\n\n  static contractFactorUnits(factorUnits: FactorUnit[]): FactorUnit[] {\n    return FactorUnit.contractExponents(factorUnits);\n  }\n\n  static reduceFactorUnits(factorUnits: FactorUnit[]): FactorUnit[] {\n    return FactorUnit.reduceExponents(factorUnits);\n  }\n\n  static unscale(\n    unit: Unit,\n    treatKiloGmAsUnscaled = true,\n    treatPrefixlessAsUnscaled = true\n  ): Unit {\n    if (!unit.scalingOf) {\n      return unit;\n    }\n    if (treatPrefixlessAsUnscaled && !unit.prefix) {\n      return unit;\n    }\n    if (treatKiloGmAsUnscaled && unit.getIriAbbreviated() === \"unit:KiloGM\") {\n      return unit;\n    }\n    return unit.scalingOf;\n  }\n\n  /**\n   * Return a list of {@link FactorUnit}s with the same exponents as the specified `factorUnits` but their base units as units.\n   *\n   * @param factorUnits the factor units to unscale\n   * @return the unscaled factor units\n   */\n  static unscaleFactorUnits(\n    factorUnits: FactorUnit[],\n    treatKiloGMAsUnscaled = true,\n    treatPrefixlessAsUnscaled = true\n  ): FactorUnit[] {\n    return factorUnits.map(\n      (fu) =>\n        new FactorUnit(\n          Qudt.unscale(\n            fu.unit,\n            treatKiloGMAsUnscaled,\n            treatPrefixlessAsUnscaled\n          ),\n          fu.exponent\n        )\n    );\n  }\n\n  /**\n   * Instantiates a QuantityValue.\n   * @param value a Decimal\n   * @param unit a Unit or unit IRI.\n   * @return a QuantityValue with the specified data\n   */\n  static quantityValue(value: Decimal, unit: Unit | string): QuantityValue {\n    if (typeof unit === \"string\") {\n      return new QuantityValue(value, Qudt.unitRequired(unit));\n    }\n    return new QuantityValue(value, unit);\n  }\n\n  /**\n   * Converts the specified value from the unit it is in (`fromUnit`) to the specified target unit (`toUnit`).\n   * @param value: a Decimal, the value to convert.\n   * @param fromUnit: a Unit or string. A string is interpreted as a Unit IRI.\n   * @param toUnit: a Unit or string. A string is interpreted as a Unit IRI.\n   * @return the resulting value\n   */\n  static convert(\n    value: Decimal,\n    fromUnit: Unit | string,\n    toUnit: Unit | string,\n    quantityKind?: QuantityKind | string\n  ): Decimal {\n    if (!fromUnit) {\n      throw \"Parameter 'fromUnit' is required\";\n    }\n    if (!toUnit) {\n      throw \"Parameter 'toUnit' is required\";\n    }\n    const from: Unit =\n      typeof fromUnit === \"string\" ? Qudt.unitRequired(fromUnit) : fromUnit;\n    const to: Unit =\n      typeof toUnit === \"string\" ? Qudt.unitRequired(toUnit) : toUnit;\n    const qk: QuantityKind | undefined = isNullish(quantityKind)\n      ? undefined\n      : typeof quantityKind === \"string\"\n      ? Qudt.quantityKindRequired(quantityKind)\n      : quantityKind;\n    return from.convert(value, to, qk);\n  }\n\n  /**\n   * Converts the specified QuantityValue from to the specified target unit (`toUnit`).\n   * @param value: a Decimal, the value to convert.\n   * @param fromUnit: a Unit or string. A string is interpreted as a Unit IRI.\n   * @param toUnit: a Unit or string. A string is interpreted as a Unit IRI.\n   * @return a QuantityValue holding the result\n   */\n  static convertQuantityValue(\n    from: QuantityValue,\n    toUnit: Unit | string,\n    quantityKind?: QuantityKind | string\n  ): QuantityValue {\n    if (!from) {\n      throw \"Parameter 'from' is required\";\n    }\n    if (!toUnit) {\n      throw \"Parameter 'toUnit' is required\";\n    }\n    const to = typeof toUnit === \"string\" ? Qudt.unitRequired(toUnit) : toUnit;\n    const qk: QuantityKind | undefined = isNullish(quantityKind)\n      ? undefined\n      : typeof quantityKind === \"string\"\n      ? Qudt.quantityKindRequired(quantityKind)\n      : quantityKind;\n    return from.convert(to, qk);\n  }\n\n  /**\n   * Returns `true` if the two units can be converted into each other.\n   * @param fromUnit a Unit or unit IRI\n   * @param toUnit a Unit or unit IRI\n   * @return a boolean indicating whether the units are convertible.\n   */\n  static isConvertible(fromUnit: Unit | string, toUnit: Unit | string) {\n    const from: Unit =\n      typeof fromUnit === \"string\" ? Qudt.unitRequired(fromUnit) : fromUnit;\n    const to: Unit =\n      typeof toUnit === \"string\" ? Qudt.unitRequired(toUnit) : toUnit;\n    return from.isConvertible(to);\n  }\n\n  /**\n   * Returns a [Unit, Decimal] tuple containing the base unit of the specified `unit`\n   * along with the scale factor needed to convert values from the base unit to\n   * the specified unit.\n   *\n   * @param unit the unit to scale to its base\n   * @return a [Unit, Decimal] tuple with the base unit and the required scale factor\n   */\n  static scaleToBaseUnit(unit: Unit): { unit: Unit; factor: Decimal } {\n    if (!unit.scalingOf) {\n      return { unit: unit, factor: ONE };\n    }\n    const baseUnit = unit.scalingOf;\n    return { unit: baseUnit, factor: unit.getConversionMultiplier(baseUnit) };\n  }\n\n  static allUnits(): Unit[] {\n    const ret = [];\n    for (const unit of config.units.values()) {\n      ret.push(unit);\n    }\n    return ret;\n  }\n\n  static allQuantityKinds(): QuantityKind[] {\n    const ret = [];\n    for (const quantityKind of config.quantityKinds.values()) {\n      ret.push(quantityKind);\n    }\n    return ret;\n  }\n\n  static allPrefixes(): Prefix[] {\n    const ret = [];\n    for (const prefix of config.prefixes.values()) {\n      ret.push(prefix);\n    }\n    return ret;\n  }\n\n  static allSystemsOfUnits(): SystemOfUnits[] {\n    const ret = [];\n    for (const prefix of config.systemsOfUnits.values()) {\n      ret.push(prefix);\n    }\n    return ret;\n  }\n\n  static allUnitsOfSystem(system: SystemOfUnits): Unit[] {\n    const ret = [];\n    for (const unit of config.units.values()) {\n      if (system.allowsUnit(unit)) {\n        ret.push(unit);\n      }\n    }\n    return ret;\n  }\n\n  /**\n   * Returns the first unit obtained using {@link #correspondingUnitsInSystem(Unit,\n   * SystemOfUnits)}.\n   *\n   * @return the unit corresponding to the specified unit in the specified systemOfUnits.\n   */\n  static correspondingUnitInSystem(\n    unit: Unit,\n    systemOfUnits: SystemOfUnits\n  ): Unit | undefined {\n    const correspondingUnits = Qudt.correspondingUnitsInSystem(\n      unit,\n      systemOfUnits\n    );\n    if (\n      typeof correspondingUnits !== \"undefined\" &&\n      correspondingUnits.length > 0\n    ) {\n      return correspondingUnits[0];\n    }\n    return undefined;\n  }\n\n  /**\n   * Gets units that correspond to the specified unit are allowed in the specified systemOfUnits.\n   * The resulting units have to\n   *\n   * <ol>\n   *   <li>have the same dimension vector as the unit\n   *   <li>share at least one quantityKind with unit\n   * </ol>\n   *\n   * and they are ascending sorted by dissimilarity in magnitude to the magnitude of the specified\n   * unit, i.e. the first unit returned is the closest in magnitude.\n   *\n   * <p>If two resulting units have the same magnitude difference from the specified one, the\n   * following comparisons are made consecutively until a difference is found:\n   *\n   * <ol>\n   *   <li>the base unit of the specified system is ranked first\n   *   <li>conversion offset closer to the one of the specified unit is ranked first\n   *   <li>the unscaled unit is ranked first\n   *   <li>the unit that has a symbol is ranked first\n   *   <li>the unit with more quantityKinds is ranked first\n   *   <li>the units are ranked by their IRIs lexicographically\n   * </ol>\n   *\n   * that is a base unit of the system is ranked first. If none or both are base units, the one\n   * with a conversion offset closer to the specified unit's conversion offset is ranked first.\n   *\n   * @param unit\n   * @param systemOfUnits\n   * @return\n   */\n  static correspondingUnitsInSystem(\n    unit: Unit,\n    systemOfUnits: SystemOfUnits\n  ): Unit[] {\n    if (systemOfUnits.allowsUnit(unit)) {\n      return [unit];\n    }\n    const elegible = Array.from(config.units.values())\n      .filter((u) => systemOfUnits.allowsUnit(u))\n      .filter((u) => u.dimensionVectorIri === unit.dimensionVectorIri);\n\n    if (elegible.length === 1) {\n      return elegible;\n    }\n    let candidates = [...elegible];\n    // get the unit that is closest in magnitude (conversionFactor)\n    // recursively check for factor units\n    candidates = candidates.filter((u) =>\n      u.quantityKinds.some((q) => arrayContains(unit.quantityKinds, q))\n    );\n    if (candidates.length === 1) {\n      return candidates;\n    }\n    candidates.sort((l: Unit, r: Unit) => {\n      const scaleDiffL = Math.abs(Qudt.scaleDifference(l, unit));\n      const scaleDiffR = Math.abs(Qudt.scaleDifference(r, unit));\n      const diff = Math.sign(scaleDiffL - scaleDiffR);\n      if (diff !== 0) {\n        return diff;\n      }\n      // tie breaker: base unit ranked before non-base unit\n      let cmp = BooleanComparator(\n        systemOfUnits.hasBaseUnit(r),\n        systemOfUnits.hasBaseUnit(l)\n      );\n      if (cmp !== 0) {\n        return cmp;\n      }\n      // tie breaker: closer offset\n      const offsetDiffL = Math.abs(Qudt.offsetDifference(l, unit));\n      const offsetDiffR = Math.abs(Qudt.offsetDifference(r, unit));\n      cmp = Math.sign(offsetDiffL - offsetDiffR);\n      if (cmp != 0) {\n        return cmp;\n      }\n      // tie breaker: perfer unit that is not scaled\n      cmp = BooleanComparator(l.isScaled(), r.isScaled());\n      if (cmp != 0) {\n        return cmp;\n      }\n      // tie breaker prefer the unit that has a symbol (it's more likely to be\n      // commonly used):\n      cmp = BooleanComparator(r.hasSymbol(), l.hasSymbol());\n      if (cmp != 0) {\n        return cmp;\n      }\n      // tie breaker: prefer unit with more quantity kinds (it's less specific)\n      cmp = NumberComparator(l.quantityKinds.length, r.quantityKinds.length);\n      if (cmp != 0) {\n        return cmp;\n      }\n      // tie breaker: lexicographically compare iris.\n      return StringComparator(l.iri, r.iri);\n    });\n    return candidates;\n  }\n\n  private static scaleDifference(u1: Unit, u2: Unit): number {\n    const u1Log10 = u1.conversionMultiplier.log(10);\n    const u2Log10 = u2.conversionMultiplier.log(10);\n    return u1Log10.toNumber() - u2Log10.toNumber();\n  }\n\n  private static offsetDifference(u1: Unit, u2: Unit) {\n    const u1Log10 = Qudt.logOrZeroRequireNonNegative(u1.conversionOffset.abs());\n    const u2Log10 = Qudt.logOrZeroRequireNonNegative(u2.conversionOffset.abs());\n    return u1Log10.toNumber() - u2Log10.toNumber();\n  }\n\n  private static logOrZeroRequireNonNegative(val: Decimal): Decimal {\n    if (val.isNegative()) {\n      throw `Cannot get logarithm of negative value ${val}`;\n    }\n    if (val.isZero()) {\n      return ZERO;\n    }\n    return val.log(10);\n  }\n}\n\ninterface LabelMatcher {\n  matchesString(searchTerm: string): boolean;\n\n  matchesLangString(searchTerm: LangString): boolean;\n\n  matchesLangStrings(searchTerms: LangString[]): boolean;\n}\n\nclass CaseInsensitiveUnderscoreIgnoringLabelMatcher implements LabelMatcher {\n  readonly compareForEquality: string;\n\n  constructor(searchTerm: string) {\n    this.compareForEquality = this.convert(searchTerm);\n  }\n\n  convert(term: string): string {\n    return term.replaceAll(\"_\", \" \").toLocaleUpperCase(\"en-US\");\n  }\n\n  matchesString(searchTerm: string): boolean {\n    return this.convert(searchTerm) === this.compareForEquality;\n  }\n\n  matchesLangString(searchTerm: LangString): boolean {\n    return this.convert(searchTerm.text) === this.compareForEquality;\n  }\n\n  matchesLangStrings(searchTerms: LangString[]): boolean {\n    return searchTerms.some(\n      (st) => this.convert(st.text) === this.compareForEquality\n    );\n  }\n}\n"]}