universal-physics-tensor 8.0.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/data/atlas/atlas.json +21 -21
- package/data/atlas/atlas.jsonld +1 -1
- package/data/atlas/diffusion.json +5 -5
- package/data/atlas/oscillators.json +7 -7
- package/data/atlas/waves.json +11 -11
- package/data/bridge-catalog.json +490 -338
- package/data/bridge-catalog.schema.json +566 -40
- package/data/quantities.json +230 -218
- package/data/quantities.schema.json +52 -0
- package/data/units.json +1149 -0
- package/data/units.schema.json +124 -0
- package/dist/atlas/association.d.ts +1 -1
- package/dist/atlas/benchmark/hybrid-retrieval.d.ts.map +1 -1
- package/dist/atlas/benchmark/hybrid-retrieval.js +2 -5
- package/dist/atlas/benchmark/hybrid-retrieval.js.map +1 -1
- package/dist/atlas/physjs-entries.generated.d.ts +1601 -220
- package/dist/atlas/physjs-entries.generated.d.ts.map +1 -1
- package/dist/atlas/physjs-entries.generated.js +1854 -345
- package/dist/atlas/physjs-entries.generated.js.map +1 -1
- package/dist/atlas/physjs-ref.d.ts +89 -89
- package/dist/atlas/physjs-ref.d.ts.map +1 -1
- package/dist/atlas/physjs-ref.js +128 -176
- package/dist/atlas/physjs-ref.js.map +1 -1
- package/dist/atlas/proof-target.d.ts +16 -8
- package/dist/atlas/proof-target.d.ts.map +1 -1
- package/dist/atlas/proof-target.js +34 -31
- package/dist/atlas/proof-target.js.map +1 -1
- package/dist/bridges/catalog-adapter.d.ts.map +1 -1
- package/dist/bridges/catalog-adapter.js +5 -5
- package/dist/bridges/catalog-adapter.js.map +1 -1
- package/dist/bridges/catalog-load.d.ts +7 -0
- package/dist/bridges/catalog-load.d.ts.map +1 -1
- package/dist/bridges/catalog-load.js +69 -13
- package/dist/bridges/catalog-load.js.map +1 -1
- package/dist/bridges/catalog-types.d.ts +51 -6
- package/dist/bridges/catalog-types.d.ts.map +1 -1
- package/dist/bridges/evaluation-errors.d.ts +76 -0
- package/dist/bridges/evaluation-errors.d.ts.map +1 -0
- package/dist/bridges/evaluation-errors.js +118 -0
- package/dist/bridges/evaluation-errors.js.map +1 -0
- package/dist/bridges/evaluator-inputs.d.ts +5 -3
- package/dist/bridges/evaluator-inputs.d.ts.map +1 -1
- package/dist/bridges/evaluator-inputs.js +24 -24
- package/dist/bridges/evaluator-inputs.js.map +1 -1
- package/dist/bridges/evaluators.d.ts +32 -7
- package/dist/bridges/evaluators.d.ts.map +1 -1
- package/dist/bridges/evaluators.js +131 -29
- package/dist/bridges/evaluators.js.map +1 -1
- package/dist/bridges/expr-parse.d.ts +11 -3
- package/dist/bridges/expr-parse.d.ts.map +1 -1
- package/dist/bridges/expr-parse.js +46 -153
- package/dist/bridges/expr-parse.js.map +1 -1
- package/dist/bridges/holds.d.ts +11 -7
- package/dist/bridges/holds.d.ts.map +1 -1
- package/dist/bridges/holds.js +113 -234
- package/dist/bridges/holds.js.map +1 -1
- package/dist/bridges/input-contract.d.ts +65 -0
- package/dist/bridges/input-contract.d.ts.map +1 -0
- package/dist/bridges/input-contract.js +97 -0
- package/dist/bridges/input-contract.js.map +1 -0
- package/dist/bridges/notices.d.ts +11 -0
- package/dist/bridges/notices.d.ts.map +1 -0
- package/dist/bridges/notices.js +22 -0
- package/dist/bridges/notices.js.map +1 -0
- package/dist/canonical/canonical-equation.d.ts +31 -0
- package/dist/canonical/canonical-equation.d.ts.map +1 -1
- package/dist/canonical/entries/atomic.d.ts.map +1 -1
- package/dist/canonical/entries/atomic.js +11 -1
- package/dist/canonical/entries/atomic.js.map +1 -1
- package/dist/canonical/entries/condensed-matter.d.ts.map +1 -1
- package/dist/canonical/entries/condensed-matter.js +3 -0
- package/dist/canonical/entries/condensed-matter.js.map +1 -1
- package/dist/canonical/entries/dimensional-classics.d.ts.map +1 -1
- package/dist/canonical/entries/dimensional-classics.js +48 -0
- package/dist/canonical/entries/dimensional-classics.js.map +1 -1
- package/dist/canonical/entries/electromagnetism.d.ts.map +1 -1
- package/dist/canonical/entries/electromagnetism.js +30 -0
- package/dist/canonical/entries/electromagnetism.js.map +1 -1
- package/dist/canonical/entries/fluids-waves.d.ts.map +1 -1
- package/dist/canonical/entries/fluids-waves.js +34 -0
- package/dist/canonical/entries/fluids-waves.js.map +1 -1
- package/dist/canonical/entries/mechanics.d.ts.map +1 -1
- package/dist/canonical/entries/mechanics.js +24 -0
- package/dist/canonical/entries/mechanics.js.map +1 -1
- package/dist/canonical/entries/nonmonomial.d.ts.map +1 -1
- package/dist/canonical/entries/nonmonomial.js +15 -4
- package/dist/canonical/entries/nonmonomial.js.map +1 -1
- package/dist/canonical/entries/relativity.d.ts.map +1 -1
- package/dist/canonical/entries/relativity.js +6 -0
- package/dist/canonical/entries/relativity.js.map +1 -1
- package/dist/canonical/entries/statistical-mechanics.d.ts.map +1 -1
- package/dist/canonical/entries/statistical-mechanics.js +19 -0
- package/dist/canonical/entries/statistical-mechanics.js.map +1 -1
- package/dist/canonical/entries/thermo-nuclear-cosmo.d.ts.map +1 -1
- package/dist/canonical/entries/thermo-nuclear-cosmo.js +15 -0
- package/dist/canonical/entries/thermo-nuclear-cosmo.js.map +1 -1
- package/dist/cases/index.d.ts +9 -2
- package/dist/cases/index.d.ts.map +1 -1
- package/dist/cases/index.js +26 -7
- package/dist/cases/index.js.map +1 -1
- package/dist/cases/kepler-rv.js +6 -6
- package/dist/cases/kepler-rv.js.map +1 -1
- package/dist/cli/commands/_discovery-opts.d.ts +2 -1
- package/dist/cli/commands/_discovery-opts.d.ts.map +1 -1
- package/dist/cli/commands/_discovery-opts.js +7 -12
- package/dist/cli/commands/_discovery-opts.js.map +1 -1
- package/dist/cli/commands/atlas.d.ts.map +1 -1
- package/dist/cli/commands/atlas.js +6 -9
- package/dist/cli/commands/atlas.js.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +1 -2
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/connectors.d.ts.map +1 -1
- package/dist/cli/commands/connectors.js +8 -9
- package/dist/cli/commands/connectors.js.map +1 -1
- package/dist/cli/commands/derive.js +9 -10
- package/dist/cli/commands/derive.js.map +1 -1
- package/dist/cli/commands/discover.d.ts.map +1 -1
- package/dist/cli/commands/discover.js +3 -4
- package/dist/cli/commands/discover.js.map +1 -1
- package/dist/cli/commands/eval.d.ts.map +1 -1
- package/dist/cli/commands/eval.js +17 -17
- package/dist/cli/commands/eval.js.map +1 -1
- package/dist/cli/commands/evaluate.d.ts +1 -35
- package/dist/cli/commands/evaluate.d.ts.map +1 -1
- package/dist/cli/commands/evaluate.js +56 -187
- package/dist/cli/commands/evaluate.js.map +1 -1
- package/dist/cli/commands/explain.d.ts.map +1 -1
- package/dist/cli/commands/explain.js +43 -38
- package/dist/cli/commands/explain.js.map +1 -1
- package/dist/cli/commands/ground.js +1 -1
- package/dist/cli/commands/ground.js.map +1 -1
- package/dist/cli/commands/map.js +3 -4
- package/dist/cli/commands/map.js.map +1 -1
- package/dist/cli/commands/metric.d.ts.map +1 -1
- package/dist/cli/commands/metric.js +3 -0
- package/dist/cli/commands/metric.js.map +1 -1
- package/dist/cli/commands/regime.js +1 -1
- package/dist/cli/commands/regime.js.map +1 -1
- package/dist/cli/commands/symbolic.d.ts.map +1 -1
- package/dist/cli/commands/symbolic.js +5 -2
- package/dist/cli/commands/symbolic.js.map +1 -1
- package/dist/cli/commands/testplan.js +3 -3
- package/dist/cli/commands/testplan.js.map +1 -1
- package/dist/cli/conventions.d.ts +6 -7
- package/dist/cli/conventions.d.ts.map +1 -1
- package/dist/cli/conventions.js +19 -28
- package/dist/cli/conventions.js.map +1 -1
- package/dist/cli/euler-guard.d.ts +13 -2
- package/dist/cli/euler-guard.d.ts.map +1 -1
- package/dist/cli/euler-guard.js +17 -3
- package/dist/cli/euler-guard.js.map +1 -1
- package/dist/cli/eval-numbers.d.ts +4 -9
- package/dist/cli/eval-numbers.d.ts.map +1 -1
- package/dist/cli/eval-numbers.js +11 -41
- package/dist/cli/eval-numbers.js.map +1 -1
- package/dist/cli/published-url.d.ts +2 -0
- package/dist/cli/published-url.d.ts.map +1 -1
- package/dist/cli/published-url.js +5 -0
- package/dist/cli/published-url.js.map +1 -1
- package/dist/cli/record-tables.d.ts +2 -1
- package/dist/cli/record-tables.d.ts.map +1 -1
- package/dist/cli/record-tables.js +8 -3
- package/dist/cli/record-tables.js.map +1 -1
- package/dist/cli-api.d.ts +45 -12
- package/dist/cli-api.d.ts.map +1 -1
- package/dist/cli-api.js +38 -11
- package/dist/cli-api.js.map +1 -1
- package/dist/composition/adjudication.d.ts +8 -30
- package/dist/composition/adjudication.d.ts.map +1 -1
- package/dist/composition/adjudication.js +15 -67
- package/dist/composition/adjudication.js.map +1 -1
- package/dist/composition/aliases.d.ts +1 -1
- package/dist/composition/aliases.d.ts.map +1 -1
- package/dist/composition/aliases.js +10 -20
- package/dist/composition/aliases.js.map +1 -1
- package/dist/composition/bridge-analysis.d.ts +4 -0
- package/dist/composition/bridge-analysis.d.ts.map +1 -1
- package/dist/composition/bridge-analysis.js +13 -14
- package/dist/composition/bridge-analysis.js.map +1 -1
- package/dist/composition/canonical-graph.d.ts +7 -9
- package/dist/composition/canonical-graph.d.ts.map +1 -1
- package/dist/composition/canonical-graph.js +35 -19
- package/dist/composition/canonical-graph.js.map +1 -1
- package/dist/composition/canonical-prefactors.d.ts +10 -29
- package/dist/composition/canonical-prefactors.d.ts.map +1 -1
- package/dist/composition/canonical-prefactors.js +15 -202
- package/dist/composition/canonical-prefactors.js.map +1 -1
- package/dist/composition/compose.js +2 -1
- package/dist/composition/compose.js.map +1 -1
- package/dist/composition/discovery.d.ts +50 -0
- package/dist/composition/discovery.d.ts.map +1 -1
- package/dist/composition/discovery.js +2 -2
- package/dist/composition/discovery.js.map +1 -1
- package/dist/composition/edge.d.ts +0 -14
- package/dist/composition/edge.d.ts.map +1 -1
- package/dist/composition/edge.js +1 -17
- package/dist/composition/edge.js.map +1 -1
- package/dist/composition/evaluate-relation.d.ts +29 -5
- package/dist/composition/evaluate-relation.d.ts.map +1 -1
- package/dist/composition/evaluate-relation.js +100 -72
- package/dist/composition/evaluate-relation.js.map +1 -1
- package/dist/composition/explain.d.ts +5 -0
- package/dist/composition/explain.d.ts.map +1 -1
- package/dist/composition/explain.js +9 -8
- package/dist/composition/explain.js.map +1 -1
- package/dist/composition/expr-eval.d.ts.map +1 -1
- package/dist/composition/expr-eval.js +2 -1
- package/dist/composition/expr-eval.js.map +1 -1
- package/dist/composition/index.d.ts +3 -1
- package/dist/composition/index.d.ts.map +1 -1
- package/dist/composition/index.js +3 -1
- package/dist/composition/index.js.map +1 -1
- package/dist/composition/proposed-bridges.d.ts.map +1 -1
- package/dist/composition/proposed-bridges.js +3 -2
- package/dist/composition/proposed-bridges.js.map +1 -1
- package/dist/composition/retrodiction.d.ts +8 -0
- package/dist/composition/retrodiction.d.ts.map +1 -1
- package/dist/composition/retrodiction.js +5 -0
- package/dist/composition/retrodiction.js.map +1 -1
- package/dist/composition/user-equation.d.ts +11 -7
- package/dist/composition/user-equation.d.ts.map +1 -1
- package/dist/composition/user-equation.js +24 -26
- package/dist/composition/user-equation.js.map +1 -1
- package/dist/core/constants.d.ts +27 -4
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +38 -5
- package/dist/core/constants.js.map +1 -1
- package/dist/core/data-file.d.ts +27 -0
- package/dist/core/data-file.d.ts.map +1 -0
- package/dist/core/data-file.js +48 -0
- package/dist/core/data-file.js.map +1 -0
- package/dist/core/json-schema.d.ts +27 -0
- package/dist/core/json-schema.d.ts.map +1 -0
- package/dist/core/json-schema.js +153 -0
- package/dist/core/json-schema.js.map +1 -0
- package/dist/core/types.d.ts +3 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +16 -14
- package/dist/core/types.js.map +1 -1
- package/dist/diff/bridge-ast-gradient.d.ts.map +1 -1
- package/dist/diff/bridge-ast-gradient.js +6 -17
- package/dist/diff/bridge-ast-gradient.js.map +1 -1
- package/dist/diff/bridge-specs.d.ts +2 -1
- package/dist/diff/bridge-specs.d.ts.map +1 -1
- package/dist/diff/bridge-specs.js +1 -1
- package/dist/diff/bridge-specs.js.map +1 -1
- package/dist/dimensional/bridge-check.d.ts.map +1 -1
- package/dist/dimensional/bridge-check.js +3 -5
- package/dist/dimensional/bridge-check.js.map +1 -1
- package/dist/dimensional/constant-rows.d.ts +54 -0
- package/dist/dimensional/constant-rows.d.ts.map +1 -0
- package/dist/dimensional/constant-rows.js +111 -0
- package/dist/dimensional/constant-rows.js.map +1 -0
- package/dist/dimensional/constants.d.ts +4 -13
- package/dist/dimensional/constants.d.ts.map +1 -1
- package/dist/dimensional/constants.js +11 -19
- package/dist/dimensional/constants.js.map +1 -1
- package/dist/dimensional/dimension-spec.d.ts +12 -3
- package/dist/dimensional/dimension-spec.d.ts.map +1 -1
- package/dist/dimensional/dimension-spec.js +114 -150
- package/dist/dimensional/dimension-spec.js.map +1 -1
- package/dist/dimensional/exact-scale.d.ts +79 -0
- package/dist/dimensional/exact-scale.d.ts.map +1 -0
- package/dist/dimensional/exact-scale.js +222 -0
- package/dist/dimensional/exact-scale.js.map +1 -0
- package/dist/dimensional/formula-names.d.ts +13 -12
- package/dist/dimensional/formula-names.d.ts.map +1 -1
- package/dist/dimensional/formula-names.js +28 -51
- package/dist/dimensional/formula-names.js.map +1 -1
- package/dist/dimensional/quantity-registry.d.ts +10 -1
- package/dist/dimensional/quantity-registry.d.ts.map +1 -1
- package/dist/dimensional/quantity-registry.js +45 -16
- package/dist/dimensional/quantity-registry.js.map +1 -1
- package/dist/dimensional/symbolic-constants.d.ts +63 -19
- package/dist/dimensional/symbolic-constants.d.ts.map +1 -1
- package/dist/dimensional/symbolic-constants.js +109 -92
- package/dist/dimensional/symbolic-constants.js.map +1 -1
- package/dist/dimensional/unit-data.d.ts +70 -0
- package/dist/dimensional/unit-data.d.ts.map +1 -0
- package/dist/dimensional/unit-data.js +135 -0
- package/dist/dimensional/unit-data.js.map +1 -0
- package/dist/dimensional/units.d.ts +87 -24
- package/dist/dimensional/units.d.ts.map +1 -1
- package/dist/dimensional/units.js +253 -276
- package/dist/dimensional/units.js.map +1 -1
- package/dist/index.d.ts +15 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/numerical/binding-value.d.ts +21 -1
- package/dist/numerical/binding-value.d.ts.map +1 -1
- package/dist/numerical/binding-value.js +123 -118
- package/dist/numerical/binding-value.js.map +1 -1
- package/dist/numerical/evaluator-uncertainty.d.ts +34 -0
- package/dist/numerical/evaluator-uncertainty.d.ts.map +1 -0
- package/dist/numerical/evaluator-uncertainty.js +117 -0
- package/dist/numerical/evaluator-uncertainty.js.map +1 -0
- package/dist/numerical/formula-contract.d.ts +4 -0
- package/dist/numerical/formula-contract.d.ts.map +1 -1
- package/dist/numerical/formula-contract.js +7 -0
- package/dist/numerical/formula-contract.js.map +1 -1
- package/dist/numerical/formula-dimension.d.ts +9 -0
- package/dist/numerical/formula-dimension.d.ts.map +1 -1
- package/dist/numerical/formula-dimension.js +64 -10
- package/dist/numerical/formula-dimension.js.map +1 -1
- package/dist/numerical/formula-mathts.d.ts.map +1 -1
- package/dist/numerical/formula-mathts.js +6 -2
- package/dist/numerical/formula-mathts.js.map +1 -1
- package/dist/numerical/gl4-integrator.d.ts.map +1 -1
- package/dist/numerical/gl4-integrator.js +3 -3
- package/dist/numerical/gl4-integrator.js.map +1 -1
- package/dist/numerical/spacetime-metrics.d.ts +4 -0
- package/dist/numerical/spacetime-metrics.d.ts.map +1 -1
- package/dist/numerical/spacetime-metrics.js +34 -4
- package/dist/numerical/spacetime-metrics.js.map +1 -1
- package/dist/relations/types.d.ts +8 -1
- package/dist/relations/types.d.ts.map +1 -1
- package/dist/relations/types.js +10 -0
- package/dist/relations/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -30,18 +30,17 @@
|
|
|
30
30
|
* splits name each reading and are refused (`mAs` is milliampere·second or
|
|
31
31
|
* metre·ampere·second). An unknown token that ends in a digit stays unknown
|
|
32
32
|
* when it is not that product.
|
|
33
|
-
*
|
|
34
|
-
* `
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* prefix.
|
|
33
|
+
* The table is data: `data/units.json`, loaded and validated by
|
|
34
|
+
* `unit-data.ts`. Every row, prefix, refused spelling, prefix letter, affine
|
|
35
|
+
* scale and spelling note is a row there, with the reason as its `comment`;
|
|
36
|
+
* this module holds the grammar. `myr` is a milliyear because `m` is the SI
|
|
37
|
+
* prefix, and `eV` takes one, so `GeV` is 10⁹ eV in joules.
|
|
38
38
|
*
|
|
39
39
|
* @module dimensional/units
|
|
40
40
|
*/
|
|
41
|
-
import { toSiDimensionVector } from '@danielsimonjr/mathts-core';
|
|
42
|
-
import { unit } from '@danielsimonjr/mathts-functions';
|
|
43
41
|
import { equals, format, multiply, power } from './algebra.js';
|
|
44
|
-
import {
|
|
42
|
+
import { addScales, decimalScale, divideScales, multiplyScales, powerScale, ratioScale, scaleToNumber, solidusSides, UNIT_SCALE, } from './exact-scale.js';
|
|
43
|
+
import { UNIT_DATA } from './unit-data.js';
|
|
45
44
|
/** Thrown for any input that does not parse as a value with a known unit. @public */
|
|
46
45
|
export class UnitError extends Error {
|
|
47
46
|
constructor(message) {
|
|
@@ -49,126 +48,93 @@ export class UnitError extends Error {
|
|
|
49
48
|
this.name = 'UnitError';
|
|
50
49
|
}
|
|
51
50
|
}
|
|
51
|
+
/** A symbol no table row and no prefix spells. @public */
|
|
52
|
+
export class UnknownUnitError extends UnitError {
|
|
53
|
+
symbol;
|
|
54
|
+
constructor(symbol) {
|
|
55
|
+
super(`unknown unit '${symbol}'`);
|
|
56
|
+
this.symbol = symbol;
|
|
57
|
+
this.name = 'UnknownUnitError';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A unit the reader recognizes and refuses: a logarithmic or ratio unit, an
|
|
62
|
+
* ambiguous name such as `ton`, or an affine temperature inside a compound.
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export class UnitRefusedError extends UnitError {
|
|
66
|
+
constructor(message) {
|
|
67
|
+
super(message);
|
|
68
|
+
this.name = 'UnitRefusedError';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** A unit expression with more than one dimensionally distinct reading. @public */
|
|
72
|
+
export class AmbiguousUnitError extends UnitError {
|
|
73
|
+
text;
|
|
74
|
+
readings;
|
|
75
|
+
constructor(text, readings) {
|
|
76
|
+
super(`'${text}' is ambiguous: ${readings.join(' or ')}`);
|
|
77
|
+
this.text = text;
|
|
78
|
+
this.readings = readings;
|
|
79
|
+
this.name = 'AmbiguousUnitError';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
52
82
|
const D = (p) => ({ L: 0, M: 0, T: 0, I: 0, Theta: 0, N: 0, J: 0, ...p });
|
|
53
|
-
const JOULE = D({ L: 2, M: 1, T: -2 });
|
|
54
83
|
const DIMENSIONLESS = D({});
|
|
55
|
-
/** symbol →
|
|
56
|
-
const UNITS =
|
|
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
|
-
['T', [1, D({ M: 1, T: -2, I: -1 }), true]],
|
|
95
|
-
// Gauss = 10⁻⁴ T. Exact, so `GPa` stays gigapascal (prefix G + Pa) and bare `G` is gauss.
|
|
96
|
-
// The spelled name is the same unit. It does not take a prefix (`kgauss` is not a kilogauss).
|
|
97
|
-
['G', [1e-4, D({ M: 1, T: -2, I: -1 }), false]],
|
|
98
|
-
['gauss', [1e-4, D({ M: 1, T: -2, I: -1 }), false]],
|
|
99
|
-
['Gauss', [1e-4, D({ M: 1, T: -2, I: -1 }), false]],
|
|
100
|
-
['bar', [1e5, D({ L: -1, M: 1, T: -2 }), true]],
|
|
101
|
-
['atm', [101325, D({ L: -1, M: 1, T: -2 }), false]],
|
|
102
|
-
['angstrom', [1e-10, D({ L: 1 }), false]],
|
|
103
|
-
['Angstrom', [1e-10, D({ L: 1 }), false]],
|
|
104
|
-
['Å', [1e-10, D({ L: 1 }), false]],
|
|
105
|
-
// IAU-style parsec; the prefix applies, so `Mpc` is a megaparsec.
|
|
106
|
-
['pc', [3.0856775814913673e16, D({ L: 1 }), true]],
|
|
107
|
-
['ly', [C_SI * 365.25 * 86400, D({ L: 1 }), false]],
|
|
108
|
-
// CGPM 1964: 1 L = 1 dm³ = 10⁻³ m³ exactly. Prefixable, so mL is a millilitre.
|
|
109
|
-
// `l` is the same litre. Bare `mL` stays the prefix; `mol/mL` competes with metre·litre.
|
|
110
|
-
['L', [1e-3, D({ L: 3 }), true]],
|
|
111
|
-
['l', [1e-3, D({ L: 3 }), true]],
|
|
112
|
-
// Thermochemical calorie: 1 cal_th = 4.184 J exactly (NIST). Not cal_IT = 4.1868 J.
|
|
113
|
-
// Prefixable, so kcal is 4184 J.
|
|
114
|
-
['cal', [4.184, JOULE, true]],
|
|
115
|
-
// International Steam Table BTU: cal_IT × (lb/g) / (°F per 1.8 °C).
|
|
116
|
-
// 4.1868 × 453.59237 / 1.8 J exactly. Not the thermochemical calorie.
|
|
117
|
-
['BTU', [(4.1868 * 453.59237) / 1.8, JOULE, false]],
|
|
118
|
-
// psi = lbf/in². lbf = 0.45359237 kg × 9.80665 m/s² (1959 pound × standard gravity).
|
|
119
|
-
// inch = 0.0254 m exactly.
|
|
120
|
-
['psi', [(0.45359237 * 9.80665) / (0.0254 * 0.0254), D({ L: -1, M: 1, T: -2 }), false]],
|
|
121
|
-
// torr = 1/760 of a standard atmosphere = 101325/760 Pa exactly. Prefixable (mtorr).
|
|
122
|
-
['torr', [101325 / 760, D({ L: -1, M: 1, T: -2 }), true]],
|
|
123
|
-
// Conventional millimetre of mercury, 133.322387415 Pa exactly (NIST SP 811).
|
|
124
|
-
// Not the torr, and not `mm` × `Hg`.
|
|
125
|
-
['mmHg', [133.322387415, D({ L: -1, M: 1, T: -2 }), false]],
|
|
126
|
-
// Poise = 0.1 Pa·s exactly. Prefixable, so cP = 10⁻³ Pa·s. Exact `P` wins over peta.
|
|
127
|
-
['P', [0.1, D({ L: -1, M: 1, T: -1 }), true]],
|
|
128
|
-
// Rankine is proportional: K = °R × 5/9. Not affine.
|
|
129
|
-
['degR', [5 / 9, D({ Theta: 1 }), false]],
|
|
130
|
-
['°R', [5 / 9, D({ Theta: 1 }), false]],
|
|
131
|
-
// Revolutions per minute = 1/60 Hz. Prefixable, so krpm is a kilorevolution per minute.
|
|
132
|
-
['rpm', [1 / 60, D({ T: -1 }), true]],
|
|
133
|
-
// Mechanical horsepower = 550 ft·lbf/s. ft = 0.3048 m.
|
|
134
|
-
['hp', [550 * 0.3048 * 0.45359237 * 9.80665, D({ L: 2, M: 1, T: -3 }), false]],
|
|
135
|
-
]);
|
|
136
|
-
const CELSIUS_OFFSET_K = 273.15;
|
|
137
|
-
const FAHRENHEIT_SCALE = 5 / 9;
|
|
138
|
-
/** Lone spellings. A compound that contains one is refused. */
|
|
139
|
-
const AFFINE = [
|
|
140
|
-
{ id: 'celsius', scale: 1, offset: CELSIUS_OFFSET_K, symbols: ['degC', '°C'] },
|
|
141
|
-
{
|
|
142
|
-
id: 'fahrenheit',
|
|
143
|
-
scale: FAHRENHEIT_SCALE,
|
|
144
|
-
offset: CELSIUS_OFFSET_K - 32 * FAHRENHEIT_SCALE,
|
|
145
|
-
symbols: ['degF', '°F'],
|
|
146
|
-
},
|
|
147
|
-
];
|
|
84
|
+
/** symbol → exact scale to SI base, dimension, prefixability, cycle count. Rows are `data/units.json`. */
|
|
85
|
+
const UNITS = UNIT_DATA.units;
|
|
86
|
+
/** Units that are a ratio or a log scale, and the reason each is not a plain factor. */
|
|
87
|
+
const REFUSED_UNITS = UNIT_DATA.refused;
|
|
88
|
+
/**
|
|
89
|
+
* Letters that are both an SI prefix and a unit of their own. Such a token
|
|
90
|
+
* reads as the prefix reading and not as the unit times the rest: `Gyr` is a
|
|
91
|
+
* gigayear. The file's `prefixLetterUnits.comment` says why `m` is not one.
|
|
92
|
+
*/
|
|
93
|
+
const PREFIX_LETTER_UNITS = UNIT_DATA.prefixLetterUnits;
|
|
94
|
+
/** Kelvin at 0 °C, exactly. */
|
|
95
|
+
const CELSIUS_OFFSET_K = UNIT_DATA.iceKelvin;
|
|
96
|
+
/**
|
|
97
|
+
* Lone spellings. A compound that contains one is refused. The schema's `id`
|
|
98
|
+
* enum is {@link AffineTemperature}, held equal by a test: the public type
|
|
99
|
+
* promises callers these ids, so a new affine scale is an API change.
|
|
100
|
+
*/
|
|
101
|
+
const AFFINE = UNIT_DATA.affine;
|
|
102
|
+
/** Kelvin at a reading of zero, exactly: 273.15 − icePoint × scale. */
|
|
103
|
+
function affineOffset(row) {
|
|
104
|
+
return addScales(CELSIUS_OFFSET_K, multiplyScales(ratioScale(-1, 1), row.icePoint, row.scale));
|
|
105
|
+
}
|
|
106
|
+
const fractionText = (s) => (s.den === 1n ? String(s.num) : `${s.num}/${s.den}`);
|
|
107
|
+
/**
|
|
108
|
+
* How a reading on an affine scale becomes kelvin, from the row's scale and
|
|
109
|
+
* ice point: `absolute: (degF − 32) × 5/9 + 273.15`, `a difference: × 5/9, no offset`.
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
export function affineReadingNote(affine, reading) {
|
|
113
|
+
const row = AFFINE.find((candidate) => candidate.id === affine);
|
|
114
|
+
const unitScale = row.scale.num === row.scale.den;
|
|
115
|
+
if (reading === 'difference')
|
|
116
|
+
return unitScale ? 'a difference: no offset' : `a difference: × ${fractionText(row.scale)}, no offset`;
|
|
117
|
+
const kelvin = String(scaleToNumber(CELSIUS_OFFSET_K));
|
|
118
|
+
if (unitScale && row.icePoint.num === 0n)
|
|
119
|
+
return `absolute: + ${kelvin}`;
|
|
120
|
+
const shifted = row.icePoint.num === 0n ? row.symbols[0] : `(${row.symbols[0]} − ${fractionText(row.icePoint)})`;
|
|
121
|
+
return `absolute: ${unitScale ? shifted : `${shifted} × ${fractionText(row.scale)}`} + ${kelvin}`;
|
|
122
|
+
}
|
|
148
123
|
function affineRow(text) {
|
|
149
|
-
return AFFINE.find((
|
|
150
|
-
}
|
|
151
|
-
const PREFIXES = new Map([
|
|
152
|
-
['Y', 1e24], ['Z', 1e21], ['E', 1e18], ['P', 1e15], ['T', 1e12], ['G', 1e9], ['M', 1e6], ['k', 1e3],
|
|
153
|
-
['h', 1e2], ['da', 1e1], ['d', 1e-1], ['c', 1e-2], ['m', 1e-3], ['u', 1e-6], ['µ', 1e-6], ['μ', 1e-6],
|
|
154
|
-
['n', 1e-9], ['p', 1e-12], ['f', 1e-15], ['a', 1e-18], ['z', 1e-21], ['y', 1e-24],
|
|
155
|
-
]);
|
|
156
|
-
function parseSymbol(sym) {
|
|
157
|
-
const parsed = trySymbol(sym);
|
|
158
|
-
if (parsed !== null)
|
|
159
|
-
return parsed;
|
|
160
|
-
throw new UnitError(`unknown unit '${sym}'`);
|
|
124
|
+
return AFFINE.find((candidate) => candidate.symbols.includes(text));
|
|
161
125
|
}
|
|
126
|
+
const PREFIXES = UNIT_DATA.prefixes;
|
|
162
127
|
function trySymbol(sym) {
|
|
163
128
|
const exact = UNITS.get(sym);
|
|
164
129
|
if (exact !== undefined)
|
|
165
|
-
return
|
|
130
|
+
return { scale: exact.scale, dim: exact.dim, cycles: exact.cycles === true ? 1 : 0 };
|
|
166
131
|
for (const [p, f] of PREFIXES) {
|
|
167
132
|
if (!sym.startsWith(p) || sym.length === p.length)
|
|
168
133
|
continue;
|
|
169
134
|
const base = UNITS.get(sym.slice(p.length));
|
|
170
|
-
if (base !== undefined && base
|
|
171
|
-
return
|
|
135
|
+
if (base !== undefined && base.prefixable) {
|
|
136
|
+
return { scale: multiplyScales(f, base.scale), dim: base.dim, cycles: base.cycles === true ? 1 : 0 };
|
|
137
|
+
}
|
|
172
138
|
}
|
|
173
139
|
return null;
|
|
174
140
|
}
|
|
@@ -199,7 +165,7 @@ function tryOneFactor(token) {
|
|
|
199
165
|
const parsed = trySymbol(base);
|
|
200
166
|
if (parsed === null || !Number.isFinite(exp))
|
|
201
167
|
return null;
|
|
202
|
-
return { base, exp, scale: parsed
|
|
168
|
+
return { base, exp, scale: parsed.scale, dim: parsed.dim, cycles: parsed.cycles };
|
|
203
169
|
}
|
|
204
170
|
function formatReading(factors) {
|
|
205
171
|
return factors.map((factor) => (factor.exp === 1 ? factor.base : `${factor.base}^${factor.exp}`)).join('·');
|
|
@@ -212,8 +178,8 @@ function attachedUnit(symbol) {
|
|
|
212
178
|
if (!symbol.startsWith(prefix) || symbol.length === prefix.length)
|
|
213
179
|
continue;
|
|
214
180
|
const base = symbol.slice(prefix.length);
|
|
215
|
-
const
|
|
216
|
-
if (
|
|
181
|
+
const unitRow = UNITS.get(base);
|
|
182
|
+
if (unitRow !== undefined && unitRow.prefixable)
|
|
217
183
|
return base;
|
|
218
184
|
}
|
|
219
185
|
return null;
|
|
@@ -254,11 +220,21 @@ function segmentations(token) {
|
|
|
254
220
|
* does not also split it. Inside a compound, a prefixed factor also competes
|
|
255
221
|
* with each heterogeneous product. A homogeneous power does not.
|
|
256
222
|
*/
|
|
257
|
-
function tokenWays(token, compete) {
|
|
223
|
+
function tokenWays(token, compete, side) {
|
|
224
|
+
const refused = REFUSED_UNITS.get(token);
|
|
225
|
+
if (refused !== undefined)
|
|
226
|
+
throw new UnitRefusedError(refused);
|
|
258
227
|
const single = tryOneFactor(token);
|
|
259
228
|
if (single !== null && !compete)
|
|
260
229
|
return [[single]];
|
|
261
|
-
|
|
230
|
+
// A prefixed numerator token is the prefix reading: `mN/m` is millinewton per metre.
|
|
231
|
+
// A denominator keeps the product readings (`W/mK`), and a target dimension chooses.
|
|
232
|
+
if (single !== null && side === 'numerator')
|
|
233
|
+
return [[single]];
|
|
234
|
+
const prefixWins = single !== null && PREFIX_LETTER_UNITS.has(token[0]) && attachedUnit(single.base) !== null;
|
|
235
|
+
const products = segmentations(token)
|
|
236
|
+
.filter((way) => way.length > 1)
|
|
237
|
+
.filter(() => !prefixWins);
|
|
262
238
|
const extra = single === null ? products : products.filter((way) => !homogeneousPower(single, way));
|
|
263
239
|
const ways = [];
|
|
264
240
|
if (single !== null)
|
|
@@ -270,7 +246,7 @@ function tokenWays(token, compete) {
|
|
|
270
246
|
unique.set(formatReading(way), way);
|
|
271
247
|
const list = [...unique.values()];
|
|
272
248
|
if (list.length === 0)
|
|
273
|
-
throw new
|
|
249
|
+
throw new UnknownUnitError(token);
|
|
274
250
|
return list;
|
|
275
251
|
}
|
|
276
252
|
function cartesian(lists) {
|
|
@@ -285,7 +261,7 @@ function cartesian(lists) {
|
|
|
285
261
|
return acc;
|
|
286
262
|
}
|
|
287
263
|
function sideTokens(text) {
|
|
288
|
-
return text.split(/[*·\s]
|
|
264
|
+
return text.split(/[*·\s]+|(?<=[A-Za-zµμΩ])\.(?=[A-Za-zµμΩ])/).filter((part) => part.length > 0);
|
|
289
265
|
}
|
|
290
266
|
function formatSide(tokens) {
|
|
291
267
|
return tokens.map((factors) => formatReading(factors)).join('·');
|
|
@@ -298,42 +274,61 @@ function formatExpression(num, den) {
|
|
|
298
274
|
return denominator.includes('·') ? `${numerator}/(${denominator})` : `${numerator}/${denominator}`;
|
|
299
275
|
}
|
|
300
276
|
function accumulate(tokens, sign) {
|
|
301
|
-
let scale =
|
|
277
|
+
let scale = UNIT_SCALE;
|
|
302
278
|
let dim = DIMENSIONLESS;
|
|
279
|
+
let cycles = 0;
|
|
303
280
|
for (const factors of tokens) {
|
|
304
281
|
for (const factor of factors) {
|
|
305
282
|
const n = sign * factor.exp;
|
|
306
|
-
scale
|
|
283
|
+
scale = multiplyScales(scale, powerScale(factor.scale, n));
|
|
307
284
|
dim = multiply(dim, power(factor.dim, n));
|
|
285
|
+
cycles += factor.cycles * n;
|
|
308
286
|
}
|
|
309
287
|
}
|
|
310
|
-
return { scale, dim };
|
|
288
|
+
return { scale, dim, cycles };
|
|
289
|
+
}
|
|
290
|
+
function reading(exact, dim, label, cycles, affine) {
|
|
291
|
+
return {
|
|
292
|
+
scale: scaleToNumber(exact),
|
|
293
|
+
exact,
|
|
294
|
+
dim,
|
|
295
|
+
label,
|
|
296
|
+
cycles,
|
|
297
|
+
...(affine === undefined ? {} : { affine }),
|
|
298
|
+
};
|
|
311
299
|
}
|
|
312
|
-
/**
|
|
300
|
+
/**
|
|
301
|
+
* Every dimensionally distinct reading of one unit expression.
|
|
302
|
+
*
|
|
303
|
+
* The unit grammar is not the formula grammar, and the difference is
|
|
304
|
+
* deliberate: a unit symbol is not an identifier (a prefix, a glued product
|
|
305
|
+
* or a glued power lives inside one token: `mN`, `m2K`, `Vs`, `K²`, `°C`,
|
|
306
|
+
* `%`), and everything after the first `/` is the denominator, the
|
|
307
|
+
* single-solidus convention of ISO 80000-1 (`W/m*K` is W·m⁻¹·K⁻¹), where the
|
|
308
|
+
* formula grammar would read `(W/m)·K`.
|
|
309
|
+
* @internal
|
|
310
|
+
*/
|
|
313
311
|
function unitReadings(text) {
|
|
314
312
|
const t = text.trim();
|
|
315
313
|
if (t === '' || t === '1')
|
|
316
|
-
return [
|
|
314
|
+
return [reading(UNIT_SCALE, DIMENSIONLESS, '1', 0)];
|
|
317
315
|
const affine = affineRow(t);
|
|
318
|
-
if (affine !== undefined)
|
|
319
|
-
return [
|
|
320
|
-
|
|
321
|
-
if (AFFINE.some((row) => row.symbols.some((symbol) => t.includes(symbol))) ||
|
|
316
|
+
if (affine !== undefined)
|
|
317
|
+
return [reading(affine.scale, D({ Theta: 1 }), t, 0, affine.id)];
|
|
318
|
+
if (AFFINE.some((candidate) => candidate.symbols.some((symbol) => t.includes(symbol))) ||
|
|
322
319
|
/deg\s+[CFcf]|°\s*[CFcf]/.test(t)) {
|
|
323
|
-
throw new
|
|
320
|
+
throw new UnitRefusedError('an affine temperature cannot be part of a compound unit; give K');
|
|
324
321
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
throw new UnitError(`'${t}' has more than one '/'; put the whole denominator after one '/'`);
|
|
322
|
+
// A chain is one denominator: `km/s/Mpc` is km/(s·Mpc), the way the Hubble constant is written.
|
|
323
|
+
const sides = solidusSides(t);
|
|
328
324
|
const compete = /[*·/\s]/.test(t);
|
|
329
|
-
const numerator = sideTokens(
|
|
330
|
-
const
|
|
331
|
-
const denominator = denominatorText === '' ? [] : sideTokens(denominatorText);
|
|
325
|
+
const numerator = sideTokens(sides.numerator);
|
|
326
|
+
const denominator = sides.denominator === '' ? [] : sideTokens(sides.denominator);
|
|
332
327
|
// `/s` is a dimensionless numerator over seconds. A bare `/` is not a unit.
|
|
333
328
|
if (numerator.length === 0 && denominator.length === 0)
|
|
334
|
-
throw new
|
|
335
|
-
const numWays = numerator.map((token) => tokenWays(token, compete));
|
|
336
|
-
const denWays = denominator.map((token) => tokenWays(token, compete));
|
|
329
|
+
throw new UnknownUnitError(t);
|
|
330
|
+
const numWays = numerator.map((token) => tokenWays(token, compete, 'numerator'));
|
|
331
|
+
const denWays = denominator.map((token) => tokenWays(token, compete, 'denominator'));
|
|
337
332
|
const numCombos = numerator.length === 0 ? [[]] : cartesian(numWays);
|
|
338
333
|
const denCombos = denominator.length === 0 ? [[]] : cartesian(denWays);
|
|
339
334
|
const unique = new Map();
|
|
@@ -342,180 +337,162 @@ function unitReadings(text) {
|
|
|
342
337
|
const n = accumulate(num, 1);
|
|
343
338
|
const d = accumulate(den, -1);
|
|
344
339
|
const label = formatExpression(num, den);
|
|
345
|
-
unique.set(label,
|
|
340
|
+
unique.set(label, reading(multiplyScales(n.scale, d.scale), multiply(n.dim, d.dim), label, n.cycles + d.cycles));
|
|
346
341
|
}
|
|
347
342
|
}
|
|
348
343
|
return [...unique.values()];
|
|
349
344
|
}
|
|
350
|
-
function ambiguousMessage(text, readings) {
|
|
351
|
-
return `'${text}' is ambiguous: ${readings.map((reading) => reading.label).join(' or ')}`;
|
|
352
|
-
}
|
|
353
345
|
const SUPERSCRIPT = { '¹': 1, '²': 2, '³': 3 };
|
|
354
346
|
/**
|
|
355
|
-
* Spellings that convert correctly and still mean something else to a reader
|
|
356
|
-
* `
|
|
357
|
-
* is
|
|
347
|
+
* Spellings that convert correctly and still mean something else to a reader:
|
|
348
|
+
* the `spellingNotes` of `data/units.json` for each symbol in `given`, in file
|
|
349
|
+
* order. `myr` is a milliyear, bare `G` is the gauss, bare `A` the ampere.
|
|
358
350
|
* @internal
|
|
359
351
|
*/
|
|
360
352
|
export function unitConventionNotes(given) {
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
if (symbols.includes('myr')) {
|
|
364
|
-
notes.push('myr is a milliyear (the SI prefix m on yr = 0.001 yr), not a million years; a million years is Myr');
|
|
365
|
-
}
|
|
366
|
-
if (symbols.includes('Msun')) {
|
|
367
|
-
notes.push(`Msun is ${M_SUN_SI} kg (M_SUN_SI), not GM☉/G; G×Msun is about 3.0e-4 high versus the IAU GM☉. Use the unit Msun_iau, or the eval name GM_sun, for GM_SUN_SI`);
|
|
368
|
-
}
|
|
369
|
-
if (symbols.includes('G'))
|
|
370
|
-
notes.push('bare G is the gauss (1e-4 T); GPa is still a gigapascal');
|
|
371
|
-
if (symbols.includes('T'))
|
|
372
|
-
notes.push('bare T is the tesla; Ts is a terasecond');
|
|
373
|
-
if (symbols.includes('A'))
|
|
374
|
-
notes.push('bare A is the ampere, not the angstrom; write angstrom or Å for 10^-10 m');
|
|
375
|
-
if (symbols.includes('P'))
|
|
376
|
-
notes.push('bare P is the poise (0.1 Pa·s); PV is still a petavolt');
|
|
377
|
-
return notes;
|
|
353
|
+
const symbols = new Set(given.split(/[*·/\s^0-9()+-]+/).filter((s) => s.length > 0));
|
|
354
|
+
return [...UNIT_DATA.spellingNotes].filter(([spelling]) => symbols.has(spelling)).map(([, note]) => note);
|
|
378
355
|
}
|
|
379
|
-
/**
|
|
380
|
-
export function
|
|
356
|
+
/** The one reading of a unit expression; more than one is refused. @internal */
|
|
357
|
+
export function readUnit(text) {
|
|
381
358
|
const t = text.trim();
|
|
382
359
|
const readings = unitReadings(t);
|
|
383
360
|
if (readings.length !== 1)
|
|
384
|
-
throw new
|
|
385
|
-
|
|
386
|
-
return {
|
|
387
|
-
scale: reading.scale,
|
|
388
|
-
dim: reading.dim,
|
|
389
|
-
...(reading.affine !== undefined ? { affine: reading.affine } : {}),
|
|
390
|
-
};
|
|
361
|
+
throw new AmbiguousUnitError(t, readings.map((r) => r.label));
|
|
362
|
+
return readings[0];
|
|
391
363
|
}
|
|
392
|
-
/**
|
|
393
|
-
export function
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
throw new UnitError(`unknown affine temperature '${id}'`);
|
|
397
|
-
return row.offset;
|
|
364
|
+
/** Parse a unit expression; the empty string is dimensionless. @public */
|
|
365
|
+
export function parseUnit(text) {
|
|
366
|
+
const r = readUnit(text);
|
|
367
|
+
return { scale: r.scale, dim: r.dim, ...(r.affine !== undefined ? { affine: r.affine } : {}) };
|
|
398
368
|
}
|
|
399
|
-
/**
|
|
400
|
-
function
|
|
369
|
+
/** The SI magnitude of `magnitude` in `unit`, exactly. An absolute affine reading adds its offset. */
|
|
370
|
+
function siMagnitude(magnitude, unit, temperature) {
|
|
371
|
+
const scaled = multiplyScales(magnitude, unit.exact);
|
|
401
372
|
const row = unit.affine === undefined ? undefined : AFFINE.find((candidate) => candidate.id === unit.affine);
|
|
402
|
-
|
|
403
|
-
|
|
373
|
+
return row !== undefined && temperature === 'absolute' ? addScales(scaled, affineOffset(row)) : scaled;
|
|
374
|
+
}
|
|
375
|
+
const FRACTION = /^\s*([+-]?(?:\d+\.?\d*|\.\d+))\s*\/\s*((?:\d+\.?\d*|\.\d+))\s*$/;
|
|
376
|
+
const NUMBER_THEN_REST = /^\s*([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)\s*(.*?)\s*$/;
|
|
377
|
+
/**
|
|
378
|
+
* `raw` split into an exact decimal magnitude and the unit text after it, or
|
|
379
|
+
* null when `raw` is not a number followed by an optional unit. A plain
|
|
380
|
+
* fraction (`1/3`) is a number. Text that begins with an operator (`*h`,
|
|
381
|
+
* `·c`) is an expression, not a unit.
|
|
382
|
+
* @internal
|
|
383
|
+
*/
|
|
384
|
+
function splitQuantityLiteral(raw) {
|
|
385
|
+
const fraction = FRACTION.exec(raw);
|
|
386
|
+
if (fraction !== null) {
|
|
387
|
+
const den = decimalScale(fraction[2]);
|
|
388
|
+
if (den.num !== 0n)
|
|
389
|
+
return { magnitude: divideScales(decimalScale(fraction[1]), den), unit: '' };
|
|
390
|
+
}
|
|
391
|
+
const m = NUMBER_THEN_REST.exec(raw);
|
|
392
|
+
if (m === null)
|
|
393
|
+
return null;
|
|
394
|
+
const unit = m[2];
|
|
395
|
+
if (unit.startsWith('*') || unit.startsWith('·'))
|
|
396
|
+
return null;
|
|
397
|
+
const magnitude = decimalScale(m[1]);
|
|
398
|
+
return magnitude === null ? null : { magnitude, unit };
|
|
399
|
+
}
|
|
400
|
+
function finiteLiteral(raw, value) {
|
|
401
|
+
if (!Number.isFinite(value))
|
|
402
|
+
throw new UnitError(`'${raw}' is not a finite number`);
|
|
403
|
+
return value;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Read `raw` as one number with an optional unit, in SI. Null when `raw` is
|
|
407
|
+
* not that form or names a unit no row spells, so the caller can read it as
|
|
408
|
+
* an expression. A refused unit and an ambiguous one throw. The value may be
|
|
409
|
+
* non-finite (`1e999`); the caller owns that refusal and its error class.
|
|
410
|
+
* @internal
|
|
411
|
+
*/
|
|
412
|
+
export function readQuantityLiteral(raw, temperature = 'absolute') {
|
|
413
|
+
const literal = splitQuantityLiteral(raw);
|
|
414
|
+
if (literal === null)
|
|
415
|
+
return null;
|
|
416
|
+
if (literal.unit === '') {
|
|
417
|
+
return { value: scaleToNumber(literal.magnitude), dim: DIMENSIONLESS, unit: '', cycles: 0 };
|
|
418
|
+
}
|
|
419
|
+
let unit;
|
|
420
|
+
try {
|
|
421
|
+
unit = readUnit(literal.unit);
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
if (error instanceof UnknownUnitError)
|
|
425
|
+
return null;
|
|
426
|
+
throw error;
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
value: scaleToNumber(siMagnitude(literal.magnitude, unit, temperature)),
|
|
430
|
+
dim: unit.dim,
|
|
431
|
+
unit: literal.unit,
|
|
432
|
+
cycles: unit.cycles,
|
|
433
|
+
...(unit.affine === undefined ? {} : { affine: unit.affine }),
|
|
434
|
+
};
|
|
404
435
|
}
|
|
405
436
|
/**
|
|
406
437
|
* Convert `raw` (`<number>[unit]`) into `target` (a unit expression). A bare
|
|
407
438
|
* number is taken to be in `target` already. The dimensions must agree.
|
|
408
439
|
*
|
|
440
|
+
* The magnitude, the unit scales and the target scale are multiplied as
|
|
441
|
+
* exact rationals and rounded once, so `1 g/cm^3` in `kg/m^3` is 1000.
|
|
442
|
+
*
|
|
409
443
|
* @returns the value in `target`, and the unit the user gave (`''` for none).
|
|
410
444
|
* @public
|
|
411
445
|
*/
|
|
412
|
-
export function convertValue(raw, target,
|
|
413
|
-
const
|
|
414
|
-
if (
|
|
446
|
+
export function convertValue(raw, target, temperature = 'absolute') {
|
|
447
|
+
const literal = splitQuantityLiteral(raw);
|
|
448
|
+
if (literal === null)
|
|
415
449
|
throw new UnitError(`'${raw}' is not a number with an optional unit`);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
throw new UnitError(`'${raw}' is not a finite number`);
|
|
420
|
-
if (given === '')
|
|
421
|
-
return { value: v, given };
|
|
450
|
+
if (literal.unit === '')
|
|
451
|
+
return { value: finiteLiteral(raw, scaleToNumber(literal.magnitude)), given: '' };
|
|
452
|
+
const given = literal.unit;
|
|
422
453
|
const readings = unitReadings(given);
|
|
423
|
-
const to =
|
|
454
|
+
const to = readUnit(target);
|
|
424
455
|
if (to.affine !== undefined)
|
|
425
456
|
throw new UnitError(`a declared unit cannot be affine ('${target}')`);
|
|
426
457
|
const matched = readings.filter((candidate) => equals(candidate.dim, to.dim));
|
|
427
458
|
if (matched.length !== 1) {
|
|
428
459
|
if (readings.length > 1)
|
|
429
|
-
throw new
|
|
460
|
+
throw new AmbiguousUnitError(given, readings.map((r) => r.label));
|
|
430
461
|
const from = readings[0];
|
|
431
462
|
if (from === undefined)
|
|
432
|
-
throw new
|
|
463
|
+
throw new UnknownUnitError(given);
|
|
433
464
|
throw new UnitError(`'${given}' is ${format(from.dim)}, but this input is ${format(to.dim)} (${target || 'dimensionless'})`);
|
|
434
465
|
}
|
|
435
466
|
const from = matched[0];
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
return { value: via, given };
|
|
444
|
-
return { value: local, given };
|
|
445
|
-
}
|
|
446
|
-
/** Spellings MathTS's unit parser accepts for the same UPT symbol. */
|
|
447
|
-
function mathTsSpelling(text) {
|
|
448
|
-
return text
|
|
449
|
-
.replaceAll('µ', 'u')
|
|
450
|
-
.replaceAll('μ', 'u')
|
|
451
|
-
.replaceAll('Ω', 'ohm')
|
|
452
|
-
.replaceAll('Å', 'angstrom')
|
|
453
|
-
.replaceAll('°C', 'degC');
|
|
454
|
-
}
|
|
455
|
-
/** The 7-base record for a MathTS length-10 exponent vector. Angle, bit, and solid angle are dropped. */
|
|
456
|
-
function dimensionFromUnitVector(vector) {
|
|
457
|
-
const v = toSiDimensionVector(vector, { ignoreExtra: true });
|
|
458
|
-
return { L: v[0], M: v[1], T: v[2], I: v[3], Theta: v[4], N: v[5], J: v[6] };
|
|
459
|
-
}
|
|
460
|
-
function mathTsReading(value, unitText) {
|
|
461
|
-
// `unit` is a typed-function; its declared return is `unknown`.
|
|
462
|
-
const created = unit(value, mathTsSpelling(unitText));
|
|
463
|
-
const n = Number(created.toSI().value);
|
|
464
|
-
if (!Number.isFinite(n))
|
|
465
|
-
throw new Error('non-finite SI magnitude');
|
|
466
|
-
return { si: n, dim: dimensionFromUnitVector(created.dimensions) };
|
|
467
|
-
}
|
|
468
|
-
function mathTsRatio(value, given, target) {
|
|
469
|
-
try {
|
|
470
|
-
const from = mathTsReading(value, given);
|
|
471
|
-
const to = mathTsReading(1, target);
|
|
472
|
-
if (to.si === 0)
|
|
473
|
-
return undefined;
|
|
474
|
-
// A scale match is not enough when the 7-base dimensions differ.
|
|
475
|
-
if (!equals(from.dim, parseUnit(given).dim))
|
|
476
|
-
return undefined;
|
|
477
|
-
if (!equals(to.dim, parseUnit(target).dim))
|
|
478
|
-
return undefined;
|
|
479
|
-
return from.si / to.si;
|
|
480
|
-
}
|
|
481
|
-
catch {
|
|
482
|
-
return undefined;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
|
-
* MathTS's SI value for `magnitude` of `unitText`, when that value and the
|
|
487
|
-
* 7-base dimension agree with `local`. Affine °C stays on the local offset.
|
|
488
|
-
* A symbol MathTS lacks, or a scale it disagrees with (`bit` is 1 there and
|
|
489
|
-
* ln 2 here; a solar mass, a Julian year, and the gauss are absent), returns
|
|
490
|
-
* undefined so the caller keeps the local table.
|
|
491
|
-
*
|
|
492
|
-
* @internal
|
|
493
|
-
*/
|
|
494
|
-
export function mathTsAgreedQuantity(magnitude, unitText, local) {
|
|
495
|
-
if (local.affine !== undefined)
|
|
496
|
-
return undefined;
|
|
497
|
-
try {
|
|
498
|
-
const read = mathTsReading(magnitude, unitText);
|
|
499
|
-
if (!sameQuantity(read.si, magnitude * local.scale))
|
|
500
|
-
return undefined;
|
|
501
|
-
if (!equals(read.dim, local.dim))
|
|
502
|
-
return undefined;
|
|
503
|
-
return { value: read.si, dim: read.dim };
|
|
504
|
-
}
|
|
505
|
-
catch {
|
|
506
|
-
return undefined;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
function sameQuantity(got, expected) {
|
|
510
|
-
const scale = Math.max(Math.abs(got), Math.abs(expected));
|
|
511
|
-
return Math.abs(got - expected) <= 1e-9 * scale;
|
|
467
|
+
const value = scaleToNumber(divideScales(siMagnitude(literal.magnitude, from, temperature), to.exact));
|
|
468
|
+
return {
|
|
469
|
+
value: finiteLiteral(raw, value),
|
|
470
|
+
given,
|
|
471
|
+
...(from.cycles === 0 ? {} : { cycles: from.cycles }),
|
|
472
|
+
...(from.affine === undefined ? {} : { affine: from.affine }),
|
|
473
|
+
};
|
|
512
474
|
}
|
|
513
475
|
/** The dimension of a declared unit expression. @internal */
|
|
514
476
|
export function unitDimension(unit) {
|
|
515
477
|
return parseUnit(unit).dim;
|
|
516
478
|
}
|
|
517
|
-
/** The
|
|
479
|
+
/** The symbols the unit table spells, with their exact scales, for the agreement test against MathTS. @internal */
|
|
480
|
+
export function unitRows() {
|
|
481
|
+
return UNITS;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* The tables conversion reads, for the CLI record's fingerprint (`src/cli/record-tables.ts`):
|
|
485
|
+
* each row's scale, dimension, prefixability and `cycles` flag (which multiplies an angular
|
|
486
|
+
* input by 2π), the prefixes, the Celsius offset, and the SHA-256 of `data/units.json`, so a
|
|
487
|
+
* refused spelling, a prefix letter, an affine row or a note that changes changes the fingerprint too.
|
|
488
|
+
* @internal
|
|
489
|
+
*/
|
|
518
490
|
export function unitTables() {
|
|
519
|
-
return {
|
|
491
|
+
return {
|
|
492
|
+
units: new Map([...UNITS].map(([symbol, r]) => [symbol, [scaleToNumber(r.scale), r.dim, r.prefixable, r.cycles === true]])),
|
|
493
|
+
prefixes: new Map([...PREFIXES].map(([prefix, f]) => [prefix, scaleToNumber(f)])),
|
|
494
|
+
celsiusOffsetK: scaleToNumber(CELSIUS_OFFSET_K),
|
|
495
|
+
sourceSha256: UNIT_DATA.sha256,
|
|
496
|
+
};
|
|
520
497
|
}
|
|
521
498
|
//# sourceMappingURL=units.js.map
|