UNPKG

@qudtlib/core

Version:

Data model for QUDTLib

1 lines 38 kB
{"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"]}