@qudtlib/core
Version:
Data model for QUDTLib
1 lines • 13 kB
Source Map (JSON)
{"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"]}