fhirsmith 0.3.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/CHANGELOG.md +42 -0
- package/FHIRsmith.png +0 -0
- package/README.md +277 -0
- package/config-template.json +144 -0
- package/library/folder-setup.js +58 -0
- package/library/html-server.js +166 -0
- package/library/html.js +835 -0
- package/library/i18nsupport.js +259 -0
- package/library/languages.js +779 -0
- package/library/logger-telnet.js +205 -0
- package/library/logger.js +279 -0
- package/library/package-manager.js +876 -0
- package/library/utilities.js +196 -0
- package/library/version-utilities.js +1056 -0
- package/npmprojector/config-example.json +13 -0
- package/npmprojector/indexer.js +394 -0
- package/npmprojector/npmprojector.js +395 -0
- package/npmprojector/readme.md +174 -0
- package/npmprojector/watcher.js +335 -0
- package/package.json +119 -0
- package/packages/package-crawler.js +846 -0
- package/packages/packages-template.html +126 -0
- package/packages/packages.js +2838 -0
- package/passwords.ini +2 -0
- package/publisher/publisher-template.html +208 -0
- package/publisher/publisher.js +2167 -0
- package/publisher/task-draft.js +458 -0
- package/registry/api.js +735 -0
- package/registry/crawler.js +637 -0
- package/registry/model.js +513 -0
- package/registry/readme.md +243 -0
- package/registry/registry-data.json +121015 -0
- package/registry/registry-template.html +126 -0
- package/registry/registry.js +1395 -0
- package/registry/test-runner.js +237 -0
- package/root-template.html +124 -0
- package/server.js +524 -0
- package/shl/private-key.pem +5 -0
- package/shl/public-key.pem +18 -0
- package/shl/shl.js +1125 -0
- package/shl/vhl.js +69 -0
- package/static/FHIRsmith128.png +0 -0
- package/static/FHIRsmith16.png +0 -0
- package/static/FHIRsmith32.png +0 -0
- package/static/FHIRsmith64.png +0 -0
- package/static/assets/css/bootstrap-fhir.css +5302 -0
- package/static/assets/css/bootstrap-glyphicons.css +2 -0
- package/static/assets/css/bootstrap.css +4097 -0
- package/static/assets/css/jquery-ui.css +523 -0
- package/static/assets/css/jquery-ui.structure.css +863 -0
- package/static/assets/css/jquery-ui.structure.min.css +5 -0
- package/static/assets/css/jquery-ui.theme.css +439 -0
- package/static/assets/css/jquery-ui.theme.min.css +5 -0
- package/static/assets/css/jquery.ui.all.css +7 -0
- package/static/assets/css/modules.css +18 -0
- package/static/assets/css/project.css +367 -0
- package/static/assets/css/pygments-manni.css +66 -0
- package/static/assets/css/tags.css +74 -0
- package/static/assets/css/xml.css +2 -0
- package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
- package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
- package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
- package/static/assets/ico/favicon.ico +0 -0
- package/static/assets/ico/favicon.png +0 -0
- package/static/assets/images/fhir-logo-www.png +0 -0
- package/static/assets/images/fhir-logo.png +0 -0
- package/static/assets/images/hl7-logo.png +0 -0
- package/static/assets/images/logo_ansinew.jpg +0 -0
- package/static/assets/images/search.png +0 -0
- package/static/assets/images/stripe.png +0 -0
- package/static/assets/images/target.png +0 -0
- package/static/assets/images/tx-registry-root.gif +0 -0
- package/static/assets/images/tx-registry.png +0 -0
- package/static/assets/images/tx-server.png +0 -0
- package/static/assets/images/tx-version.png +0 -0
- package/static/assets/js/bootstrap.min.js +6 -0
- package/static/assets/js/fhir-gw.js +259 -0
- package/static/assets/js/fhir.js +2 -0
- package/static/assets/js/html5shiv.js +8 -0
- package/static/assets/js/jcookie.js +96 -0
- package/static/assets/js/jquery-ui.min.js +6 -0
- package/static/assets/js/jquery.js +10716 -0
- package/static/assets/js/jquery.min.js +2 -0
- package/static/assets/js/jquery.ui.core.js +314 -0
- package/static/assets/js/jquery.ui.draggable.js +825 -0
- package/static/assets/js/jquery.ui.mouse.js +162 -0
- package/static/assets/js/jquery.ui.resizable.js +842 -0
- package/static/assets/js/jquery.ui.widget.js +268 -0
- package/static/assets/js/json2.js +487 -0
- package/static/assets/js/jtip.js +97 -0
- package/static/assets/js/respond.min.js +6 -0
- package/static/assets/js/statuspage.js +70 -0
- package/static/assets/js/xml.js +2 -0
- package/static/dist/js/bootstrap.js +1964 -0
- package/static/favicon.png +0 -0
- package/static/fhir.css +626 -0
- package/static/icon-fhir-16.png +0 -0
- package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
- package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- package/static/images/ui-icons_222222_256x240.png +0 -0
- package/static/images/ui-icons_228ef1_256x240.png +0 -0
- package/static/images/ui-icons_ef8c08_256x240.png +0 -0
- package/static/images/ui-icons_ffd27a_256x240.png +0 -0
- package/static/images/ui-icons_ffffff_256x240.png +0 -0
- package/static/js/jquery.effects.blind.js +49 -0
- package/static/js/jquery.effects.bounce.js +78 -0
- package/static/js/jquery.effects.clip.js +54 -0
- package/static/js/jquery.effects.core.js +763 -0
- package/static/js/jquery.effects.drop.js +50 -0
- package/static/js/jquery.effects.explode.js +79 -0
- package/static/js/jquery.effects.fade.js +32 -0
- package/static/js/jquery.effects.fold.js +56 -0
- package/static/js/jquery.effects.highlight.js +50 -0
- package/static/js/jquery.effects.pulsate.js +51 -0
- package/static/js/jquery.effects.scale.js +178 -0
- package/static/js/jquery.effects.shake.js +57 -0
- package/static/js/jquery.effects.slide.js +50 -0
- package/static/js/jquery.effects.transfer.js +45 -0
- package/static/js/jquery.ui.accordion.js +611 -0
- package/static/js/jquery.ui.autocomplete.js +612 -0
- package/static/js/jquery.ui.button.js +416 -0
- package/static/js/jquery.ui.datepicker.js +1823 -0
- package/static/js/jquery.ui.dialog.js +878 -0
- package/static/js/jquery.ui.droppable.js +296 -0
- package/static/js/jquery.ui.position.js +252 -0
- package/static/js/jquery.ui.progressbar.js +109 -0
- package/static/js/jquery.ui.selectable.js +266 -0
- package/static/js/jquery.ui.slider.js +666 -0
- package/static/js/jquery.ui.sortable.js +1077 -0
- package/static/js/jquery.ui.tabs.js +758 -0
- package/stats.js +80 -0
- package/test-cache/vsac/vsac-valuesets.db +0 -0
- package/token/nginx_passport_setup.md +383 -0
- package/token/security_guide.md +294 -0
- package/token/token-template.html +330 -0
- package/token/token.js +1300 -0
- package/translations/Messages.properties +1510 -0
- package/translations/Messages_ar.properties +1399 -0
- package/translations/Messages_de.properties +836 -0
- package/translations/Messages_es.properties +737 -0
- package/translations/Messages_fr.properties +1 -0
- package/translations/Messages_ja.properties +893 -0
- package/translations/Messages_nl.properties +1357 -0
- package/translations/Messages_pt.properties +1302 -0
- package/translations/Messages_ru.properties +1 -0
- package/translations/Messages_uz.properties +1 -0
- package/translations/Messages_zh.properties +1 -0
- package/translations/rendering-phrases.properties +1128 -0
- package/translations/rendering-phrases_ar.properties +1091 -0
- package/translations/rendering-phrases_de.properties +6 -0
- package/translations/rendering-phrases_es.properties +6 -0
- package/translations/rendering-phrases_fr.properties +624 -0
- package/translations/rendering-phrases_ja.properties +21 -0
- package/translations/rendering-phrases_nl.properties +970 -0
- package/translations/rendering-phrases_pt.properties +1020 -0
- package/translations/rendering-phrases_ru.properties +1094 -0
- package/translations/rendering-phrases_uz.properties +1 -0
- package/translations/rendering-phrases_zh.properties +1 -0
- package/tx/README.md +418 -0
- package/tx/cm/cm-api.js +110 -0
- package/tx/cm/cm-database.js +735 -0
- package/tx/cm/cm-package.js +325 -0
- package/tx/cs/cs-api.js +789 -0
- package/tx/cs/cs-areacode.js +615 -0
- package/tx/cs/cs-country.js +1110 -0
- package/tx/cs/cs-cpt.js +785 -0
- package/tx/cs/cs-cs.js +1579 -0
- package/tx/cs/cs-currency.js +539 -0
- package/tx/cs/cs-db.js +1321 -0
- package/tx/cs/cs-hgvs.js +329 -0
- package/tx/cs/cs-lang.js +465 -0
- package/tx/cs/cs-loinc.js +1485 -0
- package/tx/cs/cs-mimetypes.js +238 -0
- package/tx/cs/cs-ndc.js +704 -0
- package/tx/cs/cs-omop.js +1025 -0
- package/tx/cs/cs-provider-api.js +43 -0
- package/tx/cs/cs-provider-list.js +37 -0
- package/tx/cs/cs-rxnorm.js +808 -0
- package/tx/cs/cs-snomed.js +1102 -0
- package/tx/cs/cs-ucum.js +514 -0
- package/tx/cs/cs-unii.js +271 -0
- package/tx/cs/cs-uri.js +218 -0
- package/tx/cs/cs-usstates.js +305 -0
- package/tx/dev.fhir.org.yml +14 -0
- package/tx/fixtures/test-cases-setup.json +18 -0
- package/tx/fixtures/test-cases.yml +16 -0
- package/tx/html/codesystem-operations.liquid +25 -0
- package/tx/html/home-metrics.liquid +247 -0
- package/tx/html/operations-form.liquid +148 -0
- package/tx/html/search-form.liquid +62 -0
- package/tx/html/tx-template.html +133 -0
- package/tx/html/valueset-operations.liquid +54 -0
- package/tx/importers/atc-to-fhir.js +316 -0
- package/tx/importers/import-loinc.module.js +1536 -0
- package/tx/importers/import-ndc.module.js +1088 -0
- package/tx/importers/import-rxnorm.module.js +898 -0
- package/tx/importers/import-sct.module.js +2457 -0
- package/tx/importers/import-unii.module.js +601 -0
- package/tx/importers/readme.md +453 -0
- package/tx/importers/subset-loinc.module.js +1081 -0
- package/tx/importers/subset-rxnorm.module.js +938 -0
- package/tx/importers/tx-import-base.js +351 -0
- package/tx/importers/tx-import-settings.js +310 -0
- package/tx/importers/tx-import.js +357 -0
- package/tx/library/canonical-resource.js +88 -0
- package/tx/library/capabilitystatement.js +292 -0
- package/tx/library/codesystem.js +774 -0
- package/tx/library/conceptmap.js +568 -0
- package/tx/library/designations.js +932 -0
- package/tx/library/errors.js +77 -0
- package/tx/library/extensions.js +117 -0
- package/tx/library/namingsystem.js +322 -0
- package/tx/library/operation-outcome.js +127 -0
- package/tx/library/parameters.js +105 -0
- package/tx/library/renderer.js +1559 -0
- package/tx/library/terminologycapabilities.js +418 -0
- package/tx/library/ucum-parsers.js +1029 -0
- package/tx/library/ucum-service.js +370 -0
- package/tx/library/ucum-types.js +1099 -0
- package/tx/library/valueset.js +543 -0
- package/tx/library.js +676 -0
- package/tx/ocl/cm-ocl.js +106 -0
- package/tx/ocl/cs-ocl.js +39 -0
- package/tx/ocl/vs-ocl.js +105 -0
- package/tx/operation-context.js +568 -0
- package/tx/params.js +613 -0
- package/tx/provider.js +403 -0
- package/tx/sct/ecl.js +1560 -0
- package/tx/sct/expressions.js +2077 -0
- package/tx/sct/structures.js +1396 -0
- package/tx/tx-html.js +1063 -0
- package/tx/tx.fhir.org.yml +39 -0
- package/tx/tx.js +927 -0
- package/tx/vs/vs-api.js +112 -0
- package/tx/vs/vs-database.js +786 -0
- package/tx/vs/vs-package.js +358 -0
- package/tx/vs/vs-vsac.js +366 -0
- package/tx/workers/batch-validate.js +129 -0
- package/tx/workers/batch.js +361 -0
- package/tx/workers/closure.js +32 -0
- package/tx/workers/expand.js +1845 -0
- package/tx/workers/lookup.js +407 -0
- package/tx/workers/metadata.js +467 -0
- package/tx/workers/operations.js +34 -0
- package/tx/workers/read.js +164 -0
- package/tx/workers/search.js +384 -0
- package/tx/workers/subsumes.js +334 -0
- package/tx/workers/translate.js +492 -0
- package/tx/workers/validate.js +2504 -0
- package/tx/workers/worker.js +904 -0
- package/tx/xml/capabilitystatement-xml.js +63 -0
- package/tx/xml/codesystem-xml.js +62 -0
- package/tx/xml/conceptmap-xml.js +65 -0
- package/tx/xml/namingsystem-xml.js +65 -0
- package/tx/xml/operationoutcome-xml.js +127 -0
- package/tx/xml/parameters-xml.js +312 -0
- package/tx/xml/terminologycapabilities-xml.js +64 -0
- package/tx/xml/valueset-xml.js +64 -0
- package/tx/xml/xml-base.js +603 -0
- package/vcl/vcl-parser.js +1098 -0
- package/vcl/vcl.js +253 -0
- package/windows-install.js +19 -0
- package/xig/xig-template.html +124 -0
- package/xig/xig.js +3049 -0
|
@@ -0,0 +1,1099 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UCUM Types - Core data structures and types for UCUM library
|
|
3
|
+
* BSD 3-Clause License
|
|
4
|
+
* Copyright (c) 2006+, Health Intersections Pty Ltd
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class Decimal {
|
|
8
|
+
constructor(value, precision = null) {
|
|
9
|
+
this.precision = 0;
|
|
10
|
+
this.scientific = false;
|
|
11
|
+
this.negative = false;
|
|
12
|
+
this.digits = '';
|
|
13
|
+
this.decimal = 0;
|
|
14
|
+
|
|
15
|
+
if (typeof value === 'number') {
|
|
16
|
+
this._setValueDecimal(value.toString());
|
|
17
|
+
} else if (typeof value === 'string') {
|
|
18
|
+
const lowerValue = value.toLowerCase();
|
|
19
|
+
if (lowerValue.includes('e')) {
|
|
20
|
+
this._setValueScientific(lowerValue);
|
|
21
|
+
} else {
|
|
22
|
+
this._setValueDecimal(lowerValue);
|
|
23
|
+
}
|
|
24
|
+
if (precision !== null) {
|
|
25
|
+
this.precision = precision;
|
|
26
|
+
}
|
|
27
|
+
} else if (typeof value === 'undefined' || value === null) {
|
|
28
|
+
// Default constructor for internal use
|
|
29
|
+
} else {
|
|
30
|
+
throw new UcumException(`Invalid decimal value: ${value}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_setValueDecimal(value) {
|
|
35
|
+
this.scientific = false;
|
|
36
|
+
let dec = -1;
|
|
37
|
+
this.negative = value.startsWith('-');
|
|
38
|
+
if (this.negative) {
|
|
39
|
+
value = value.substring(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Remove leading zeros
|
|
43
|
+
while (value.startsWith('0') && value.length > 1) {
|
|
44
|
+
value = value.substring(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Find decimal point and validate
|
|
48
|
+
for (let i = 0; i < value.length; i++) {
|
|
49
|
+
if (value.charAt(i) === '.' && dec === -1) {
|
|
50
|
+
dec = i;
|
|
51
|
+
} else if (!/\d/.test(value.charAt(i))) {
|
|
52
|
+
throw new UcumException(`'${value}' is not a valid decimal`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (dec === -1) {
|
|
57
|
+
this.precision = value.length;
|
|
58
|
+
this.decimal = value.length;
|
|
59
|
+
this.digits = value;
|
|
60
|
+
} else if (dec === value.length - 1) {
|
|
61
|
+
throw new UcumException(`'${value}' is not a valid decimal`);
|
|
62
|
+
} else {
|
|
63
|
+
this.decimal = dec;
|
|
64
|
+
if (this._allZeros(value, 1)) {
|
|
65
|
+
this.precision = value.length - 1;
|
|
66
|
+
} else {
|
|
67
|
+
this.precision = this._countSignificants(value);
|
|
68
|
+
}
|
|
69
|
+
this.digits = this._delete(value, this.decimal, 1);
|
|
70
|
+
if (this._allZeros(this.digits, 0)) {
|
|
71
|
+
this.precision++;
|
|
72
|
+
} else {
|
|
73
|
+
while (this.digits.charAt(0) === '0') {
|
|
74
|
+
this.digits = this.digits.substring(1);
|
|
75
|
+
this.decimal--;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_setValueScientific(value) {
|
|
82
|
+
const eIndex = value.indexOf('e');
|
|
83
|
+
const s = value.substring(0, eIndex);
|
|
84
|
+
let e = value.substring(eIndex + 1);
|
|
85
|
+
if (e.startsWith('+')) {
|
|
86
|
+
e = e.substring(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!s || s === '-' || !this._isDecimal(s)) {
|
|
90
|
+
throw new UcumException(`'${value}' is not a valid decimal (numeric)`);
|
|
91
|
+
}
|
|
92
|
+
if (!e || e === '-' || !this._isInteger(e)) {
|
|
93
|
+
throw new UcumException(`'${value}' is not a valid decimal (exponent)`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this._setValueDecimal(s);
|
|
97
|
+
this.scientific = true;
|
|
98
|
+
|
|
99
|
+
// Adjust for exponent
|
|
100
|
+
const exponent = parseInt(e);
|
|
101
|
+
this.decimal = this.decimal + exponent;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
_allZeros(s, start) {
|
|
105
|
+
for (let i = start; i < s.length; i++) {
|
|
106
|
+
if (s.charAt(i) !== '0') {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
_countSignificants(value) {
|
|
114
|
+
const i = value.indexOf('.');
|
|
115
|
+
if (i > -1) {
|
|
116
|
+
value = this._delete(value, i, 1);
|
|
117
|
+
}
|
|
118
|
+
while (value.charAt(0) === '0') {
|
|
119
|
+
value = value.substring(1);
|
|
120
|
+
}
|
|
121
|
+
return value.length;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
_delete(value, offset, length) {
|
|
125
|
+
if (offset === 0) {
|
|
126
|
+
return value.substring(length);
|
|
127
|
+
} else {
|
|
128
|
+
return value.substring(0, offset) + value.substring(offset + length);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_isDecimal(s) {
|
|
133
|
+
const decimal = /^-?\d*\.?\d+$/;
|
|
134
|
+
return decimal.test(s);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
_isInteger(s) {
|
|
138
|
+
const integer = /^-?\d+$/;
|
|
139
|
+
return integer.test(s);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_stringMultiply(c, count) {
|
|
143
|
+
return c.repeat(Math.max(0, count));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_trimLeadingZeros(s) {
|
|
147
|
+
if (s === null || s === undefined) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let i = 0;
|
|
152
|
+
while (i < s.length && s.charAt(i) === '0') {
|
|
153
|
+
i++;
|
|
154
|
+
}
|
|
155
|
+
if (i === s.length) {
|
|
156
|
+
return "0";
|
|
157
|
+
} else {
|
|
158
|
+
return s.substring(i);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_stringAddition(s1, s2) {
|
|
163
|
+
if (s1.length !== s2.length) {
|
|
164
|
+
throw new Error("String lengths must be equal for addition");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const result = new Array(s2.length);
|
|
168
|
+
for (let i = 0; i < s2.length; i++) {
|
|
169
|
+
result[i] = '0';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let c = 0;
|
|
173
|
+
for (let i = s1.length - 1; i >= 0; i--) {
|
|
174
|
+
const t = c + this._dig(s1.charAt(i)) + this._dig(s2.charAt(i));
|
|
175
|
+
result[i] = this._cdig(t % 10);
|
|
176
|
+
c = Math.floor(t / 10);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// For the tens array calculation, we expect no final carry
|
|
180
|
+
if (c !== 0) {
|
|
181
|
+
// This could happen during tens array setup - just prepend the carry
|
|
182
|
+
return this._cdig(c) + result.join('');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return result.join('');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
_stringSubtraction(s1, s2) {
|
|
189
|
+
if (s1.length !== s2.length) {
|
|
190
|
+
throw new Error("String lengths must be equal for subtraction");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const result = new Array(s2.length);
|
|
194
|
+
for (let i = 0; i < s2.length; i++) {
|
|
195
|
+
result[i] = '0';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let s1Array = s1.split('');
|
|
199
|
+
|
|
200
|
+
for (let i = s1.length - 1; i >= 0; i--) {
|
|
201
|
+
let t = this._dig(s1Array[i]) - this._dig(s2.charAt(i));
|
|
202
|
+
if (t < 0) {
|
|
203
|
+
t = t + 10;
|
|
204
|
+
if (i === 0) {
|
|
205
|
+
throw new Error("internal logic error");
|
|
206
|
+
} else {
|
|
207
|
+
s1Array[i - 1] = this._cdig(this._dig(s1Array[i - 1]) - 1);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
result[i] = this._cdig(t);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return result.join('');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
_insert(ins, value, offset) {
|
|
217
|
+
if (offset === 0) {
|
|
218
|
+
return ins + value;
|
|
219
|
+
} else {
|
|
220
|
+
return value.substring(0, offset) + ins + value.substring(offset);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
_dig(c) {
|
|
225
|
+
return c.charCodeAt(0) - '0'.charCodeAt(0);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_cdig(i) {
|
|
229
|
+
return String.fromCharCode(i + '0'.charCodeAt(0));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
toString() {
|
|
233
|
+
return this.asDecimal();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
copy() {
|
|
237
|
+
const result = new Decimal();
|
|
238
|
+
result.precision = this.precision;
|
|
239
|
+
result.scientific = this.scientific;
|
|
240
|
+
result.negative = this.negative;
|
|
241
|
+
result.digits = this.digits;
|
|
242
|
+
result.decimal = this.decimal;
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static zero() {
|
|
247
|
+
return new Decimal('0');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
isZero() {
|
|
251
|
+
return this._allZeros(this.digits, 0);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static one() {
|
|
255
|
+
return new Decimal('1');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
isOne() {
|
|
259
|
+
const one = Decimal.one();
|
|
260
|
+
return this.comparesTo(one) === 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
equals(other) {
|
|
264
|
+
const o = Utilities._ensureDecimal(other);
|
|
265
|
+
return this.asDecimal() === o.asDecimal();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
comparesTo(other) {
|
|
269
|
+
if (other === null || other === undefined) {
|
|
270
|
+
return 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (this.negative && !other.negative) {
|
|
274
|
+
return -1;
|
|
275
|
+
} else if (!this.negative && other.negative) {
|
|
276
|
+
return 1;
|
|
277
|
+
} else {
|
|
278
|
+
const max = Math.max(this.decimal, other.decimal);
|
|
279
|
+
let s1 = this._stringMultiply('0', max - this.decimal + 1) + this.digits;
|
|
280
|
+
let s2 = this._stringMultiply('0', max - other.decimal + 1) + other.digits;
|
|
281
|
+
|
|
282
|
+
if (s1.length < s2.length) {
|
|
283
|
+
s1 = s1 + this._stringMultiply('0', s2.length - s1.length);
|
|
284
|
+
} else if (s2.length < s1.length) {
|
|
285
|
+
s2 = s2 + this._stringMultiply('0', s1.length - s2.length);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
let result = s1.localeCompare(s2);
|
|
289
|
+
if (this.negative) {
|
|
290
|
+
result = -result;
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
isWholeNumber() {
|
|
297
|
+
return !this.asDecimal().includes('.');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
asDecimal() {
|
|
301
|
+
let result = this.digits;
|
|
302
|
+
if (this.decimal !== this.digits.length) {
|
|
303
|
+
if (this.decimal < 0) {
|
|
304
|
+
result = '0.' + this._stringMultiply('0', 0 - this.decimal) + this.digits;
|
|
305
|
+
} else if (this.decimal < result.length) {
|
|
306
|
+
if (this.decimal === 0) {
|
|
307
|
+
result = '0.' + result;
|
|
308
|
+
} else {
|
|
309
|
+
result = this._insert('.', result, this.decimal);
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
result = result + this._stringMultiply('0', this.decimal - result.length);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (this.negative && !this._allZeros(result, 0)) {
|
|
316
|
+
result = '-' + result;
|
|
317
|
+
}
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
asInteger() {
|
|
322
|
+
if (!this.isWholeNumber()) {
|
|
323
|
+
throw new UcumException(`Unable to represent ${this.toString()} as an integer`);
|
|
324
|
+
}
|
|
325
|
+
return parseInt(this.asDecimal());
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
multiply(other) {
|
|
329
|
+
if (other === null || other === undefined) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (this.isZero() || other.isZero()) {
|
|
334
|
+
return Decimal.zero();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const max = Math.max(this.decimal, other.decimal);
|
|
338
|
+
let s1 = this._stringMultiply('0', max - this.decimal + 1) + this.digits;
|
|
339
|
+
let s2 = this._stringMultiply('0', max - other.decimal + 1) + other.digits;
|
|
340
|
+
|
|
341
|
+
if (s1.length < s2.length) {
|
|
342
|
+
s1 = s1 + this._stringMultiply('0', s2.length - s1.length);
|
|
343
|
+
} else if (s2.length < s1.length) {
|
|
344
|
+
s2 = s2 + this._stringMultiply('0', s1.length - s2.length);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (s2.localeCompare(s1) > 0) {
|
|
348
|
+
const s3 = s1;
|
|
349
|
+
s1 = s2;
|
|
350
|
+
s2 = s3;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const s = new Array(s2.length);
|
|
354
|
+
|
|
355
|
+
let t = 0;
|
|
356
|
+
for (let i = s2.length - 1; i >= 0; i--) {
|
|
357
|
+
s[i] = this._stringMultiply('0', s2.length - (i + 1));
|
|
358
|
+
let c = 0;
|
|
359
|
+
for (let j = s1.length - 1; j >= 0; j--) {
|
|
360
|
+
t = c + (this._dig(s1.charAt(j)) * this._dig(s2.charAt(i)));
|
|
361
|
+
s[i] = this._insert(String(this._cdig(t % 10)), s[i], 0);
|
|
362
|
+
c = Math.floor(t / 10);
|
|
363
|
+
}
|
|
364
|
+
while (c > 0) {
|
|
365
|
+
s[i] = this._insert(String(this._cdig(t % 10)), s[i], 0);
|
|
366
|
+
c = Math.floor(t / 10);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
t = 0;
|
|
371
|
+
for (const sv of s) {
|
|
372
|
+
t = Math.max(t, sv.length);
|
|
373
|
+
}
|
|
374
|
+
for (let i = 0; i < s.length; i++) {
|
|
375
|
+
s[i] = this._stringMultiply('0', t - s[i].length) + s[i];
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
let res = "";
|
|
379
|
+
let c = 0;
|
|
380
|
+
for (let i = t - 1; i >= 0; i--) {
|
|
381
|
+
for (let j = 0; j < s.length; j++) {
|
|
382
|
+
c = c + this._dig(s[j].charAt(i));
|
|
383
|
+
}
|
|
384
|
+
res = this._insert(String(this._cdig(c % 10)), res, 0);
|
|
385
|
+
c = Math.floor(c / 10);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (c > 0) {
|
|
389
|
+
throw new Error("internal logic error");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
let dec = res.length - ((s1.length - (max + 1)) * 2);
|
|
393
|
+
|
|
394
|
+
while (res && res !== "0" && res.startsWith("0")) {
|
|
395
|
+
res = res.substring(1);
|
|
396
|
+
dec--;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
let prec = 0;
|
|
400
|
+
if (this.isWholeNumber() && other.isWholeNumber()) {
|
|
401
|
+
// at least the specified precision, and possibly more
|
|
402
|
+
prec = Math.max(
|
|
403
|
+
Math.max(this.digits.length, other.digits.length),
|
|
404
|
+
Math.min(this.precision, other.precision)
|
|
405
|
+
);
|
|
406
|
+
} else if (this.isWholeNumber()) {
|
|
407
|
+
prec = other.precision;
|
|
408
|
+
} else if (other.isWholeNumber()) {
|
|
409
|
+
prec = this.precision;
|
|
410
|
+
} else {
|
|
411
|
+
prec = Math.min(this.precision, other.precision);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
while (res.length > prec && res.charAt(res.length - 1) === '0') {
|
|
415
|
+
res = this._delete(res, res.length - 1, 1);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const result = new Decimal();
|
|
419
|
+
result._setValueDecimal(res);
|
|
420
|
+
result.precision = prec;
|
|
421
|
+
result.decimal = dec;
|
|
422
|
+
result.negative = this.negative !== other.negative;
|
|
423
|
+
result.scientific = this.scientific || other.scientific;
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
divide(other) {
|
|
428
|
+
if (other === null || other === undefined) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (this.isZero()) {
|
|
433
|
+
return Decimal.zero();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (other.isZero()) {
|
|
437
|
+
throw new UcumException(`Attempt to divide ${this.toString()} by zero`);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const s = "0" + other.digits;
|
|
441
|
+
const m = Math.max(this.digits.length, other.digits.length) + 40; // max loops we'll do
|
|
442
|
+
const tens = new Array(10);
|
|
443
|
+
|
|
444
|
+
// Create multiples of the divisor (1x, 2x, 3x, ... 9x)
|
|
445
|
+
tens[0] = this._stringAddition(this._stringMultiply('0', s.length), s);
|
|
446
|
+
for (let i = 1; i < 10; i++) {
|
|
447
|
+
tens[i] = this._stringAddition(tens[i - 1], s);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
let v = this.digits;
|
|
451
|
+
let r = "";
|
|
452
|
+
let l = 0;
|
|
453
|
+
let d = (this.digits.length - this.decimal + 1) - (other.digits.length - other.decimal + 1);
|
|
454
|
+
|
|
455
|
+
while (v.length < tens[0].length) {
|
|
456
|
+
v = v + "0";
|
|
457
|
+
d++;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
let w;
|
|
461
|
+
let vi;
|
|
462
|
+
if (v.substring(0, other.digits.length).localeCompare(other.digits) < 0) {
|
|
463
|
+
if (v.length === tens[0].length) {
|
|
464
|
+
v = v + '0';
|
|
465
|
+
d++;
|
|
466
|
+
}
|
|
467
|
+
w = v.substring(0, other.digits.length + 1);
|
|
468
|
+
vi = w.length;
|
|
469
|
+
} else {
|
|
470
|
+
w = "0" + v.substring(0, other.digits.length);
|
|
471
|
+
vi = w.length - 1;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
let handled = false;
|
|
475
|
+
let proc;
|
|
476
|
+
|
|
477
|
+
while (!(handled && ((l > m) || ((vi >= v.length) && ((!w || this._allZeros(w, 0))))))) {
|
|
478
|
+
l++;
|
|
479
|
+
handled = true;
|
|
480
|
+
proc = false;
|
|
481
|
+
|
|
482
|
+
for (let i = 8; i >= 0; i--) {
|
|
483
|
+
if (tens[i].localeCompare(w) <= 0) {
|
|
484
|
+
proc = true;
|
|
485
|
+
r = r + this._cdig(i + 1);
|
|
486
|
+
w = this._trimLeadingZeros(this._stringSubtraction(w, tens[i]));
|
|
487
|
+
|
|
488
|
+
if (!(handled && ((l > m) || ((vi >= v.length) && ((!w || this._allZeros(w, 0))))))) {
|
|
489
|
+
if (vi < v.length) {
|
|
490
|
+
w = w + v.charAt(vi);
|
|
491
|
+
vi++;
|
|
492
|
+
handled = false;
|
|
493
|
+
} else {
|
|
494
|
+
w = w + '0';
|
|
495
|
+
d++;
|
|
496
|
+
}
|
|
497
|
+
while (w.length < tens[0].length) {
|
|
498
|
+
w = '0' + w;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (!proc) {
|
|
506
|
+
if (w.charAt(0) !== '0') {
|
|
507
|
+
throw new Error("Expected leading zero");
|
|
508
|
+
}
|
|
509
|
+
w = this._delete(w, 0, 1);
|
|
510
|
+
r = r + "0";
|
|
511
|
+
|
|
512
|
+
if (!(handled && ((l > m) || ((vi >= v.length) && ((!w || this._allZeros(w, 0))))))) {
|
|
513
|
+
if (vi < v.length) {
|
|
514
|
+
w = w + v.charAt(vi);
|
|
515
|
+
vi++;
|
|
516
|
+
handled = false;
|
|
517
|
+
} else {
|
|
518
|
+
w = w + '0';
|
|
519
|
+
d++;
|
|
520
|
+
}
|
|
521
|
+
while (w.length < tens[0].length) {
|
|
522
|
+
w = '0' + w;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
let prec;
|
|
529
|
+
|
|
530
|
+
if (this.isWholeNumber() && other.isWholeNumber() && (l < m)) {
|
|
531
|
+
for (let i = 0; i < d; i++) {
|
|
532
|
+
if (r.charAt(r.length - 1) === '0') {
|
|
533
|
+
r = this._delete(r, r.length - 1, 1);
|
|
534
|
+
d--;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
prec = 100;
|
|
538
|
+
} else {
|
|
539
|
+
if (this.isWholeNumber() && other.isWholeNumber()) {
|
|
540
|
+
prec = Math.max(this.digits.length, other.digits.length);
|
|
541
|
+
} else if (this.isWholeNumber()) {
|
|
542
|
+
prec = Math.max(other.precision, r.length - d);
|
|
543
|
+
} else if (other.isWholeNumber()) {
|
|
544
|
+
prec = Math.max(this.precision, r.length - d);
|
|
545
|
+
} else {
|
|
546
|
+
prec = Math.max(Math.min(this.precision, other.precision), r.length - d);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (r.length > prec) {
|
|
550
|
+
d = d - (r.length - prec);
|
|
551
|
+
const dig = r.charAt(prec);
|
|
552
|
+
const up = dig >= '5';
|
|
553
|
+
|
|
554
|
+
if (up) {
|
|
555
|
+
const rs = r.substring(0, prec).split('');
|
|
556
|
+
let i = rs.length - 1;
|
|
557
|
+
let carry = true;
|
|
558
|
+
|
|
559
|
+
while (carry && i >= 0) {
|
|
560
|
+
let ls = rs[i];
|
|
561
|
+
if (ls === '9') {
|
|
562
|
+
rs[i] = '0';
|
|
563
|
+
} else {
|
|
564
|
+
ls = this._cdig(this._dig(ls) + 1);
|
|
565
|
+
rs[i] = ls;
|
|
566
|
+
carry = false;
|
|
567
|
+
}
|
|
568
|
+
i--;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (carry) {
|
|
572
|
+
r = "1" + rs.join('');
|
|
573
|
+
d++; // cause we added one at the start
|
|
574
|
+
} else {
|
|
575
|
+
r = rs.join('');
|
|
576
|
+
}
|
|
577
|
+
} else {
|
|
578
|
+
r = r.substring(0, prec);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const result = new Decimal();
|
|
584
|
+
result._setValueDecimal(r);
|
|
585
|
+
result.decimal = r.length - d;
|
|
586
|
+
result.negative = this.negative !== other.negative;
|
|
587
|
+
result.precision = prec;
|
|
588
|
+
result.scientific = this.scientific || other.scientific;
|
|
589
|
+
return result;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
add(other) {
|
|
593
|
+
if (other === null || other === undefined) {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Simplified addition - convert to numbers and create new Decimal
|
|
598
|
+
const thisVal = parseFloat(this.asDecimal());
|
|
599
|
+
const otherVal = parseFloat(other.asDecimal());
|
|
600
|
+
const result = thisVal + otherVal;
|
|
601
|
+
|
|
602
|
+
const newDecimal = new Decimal(result.toString());
|
|
603
|
+
newDecimal.scientific = this.scientific || other.scientific;
|
|
604
|
+
|
|
605
|
+
// Handle precision
|
|
606
|
+
if (this.decimal < other.decimal) {
|
|
607
|
+
newDecimal.precision = this.precision;
|
|
608
|
+
} else if (other.decimal < this.decimal) {
|
|
609
|
+
newDecimal.precision = other.precision;
|
|
610
|
+
} else {
|
|
611
|
+
newDecimal.precision = Math.min(this.precision, other.precision);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return newDecimal;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
subtract(other) {
|
|
618
|
+
if (other === null || other === undefined) {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Simplified subtraction - convert to numbers and create new Decimal
|
|
623
|
+
const thisVal = parseFloat(this.asDecimal());
|
|
624
|
+
const otherVal = parseFloat(other.asDecimal());
|
|
625
|
+
const result = thisVal - otherVal;
|
|
626
|
+
|
|
627
|
+
const newDecimal = new Decimal(result.toString());
|
|
628
|
+
newDecimal.scientific = this.scientific || other.scientific;
|
|
629
|
+
|
|
630
|
+
// Handle precision similar to add
|
|
631
|
+
if (this.decimal < other.decimal) {
|
|
632
|
+
newDecimal.precision = this.precision;
|
|
633
|
+
} else if (other.decimal < this.decimal) {
|
|
634
|
+
newDecimal.precision = other.precision;
|
|
635
|
+
} else {
|
|
636
|
+
newDecimal.precision = Math.min(this.precision, other.precision);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
return newDecimal;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
checkForCouldBeWholeNumber() {
|
|
643
|
+
// whole numbers are tricky - they have implied infinite precision, but we need to check for digit errors in the last couple of digits
|
|
644
|
+
// it's a whole number if all but the last one or two digits after the decimal place is 9 or 0 and the precision is >17 (arbitrary but enough)
|
|
645
|
+
if (this.precision > 17 && this.digits.length > 3) {
|
|
646
|
+
let i = this.digits.length - 2;
|
|
647
|
+
const ch = this.digits.charAt(i); // second last character
|
|
648
|
+
if (ch === '9') {
|
|
649
|
+
while (i > 0 && this.digits.charAt(i - 1) === '9') {
|
|
650
|
+
i--;
|
|
651
|
+
}
|
|
652
|
+
if (i > 0 && i < this.digits.length - 3) {
|
|
653
|
+
this.digits = this.digits.substring(0, i - 1) + String.fromCharCode(this.digits.charCodeAt(i - 1) + 1);
|
|
654
|
+
this.precision = this.digits.length;
|
|
655
|
+
}
|
|
656
|
+
} else if (ch === '0') {
|
|
657
|
+
while (i > 0 && this.digits.charAt(i - 1) === '0') {
|
|
658
|
+
i--;
|
|
659
|
+
}
|
|
660
|
+
if (i > 0 && i < this.digits.length - 3) {
|
|
661
|
+
this.digits = this.digits.substring(0, i);
|
|
662
|
+
this.precision = this.digits.length;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Enums
|
|
670
|
+
const ConceptKind = {
|
|
671
|
+
PREFIX: 'prefix',
|
|
672
|
+
BASEUNIT: 'base-unit',
|
|
673
|
+
UNIT: 'unit'
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const Operator = {
|
|
677
|
+
MULTIPLICATION: '*',
|
|
678
|
+
DIVISION: '/'
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
const TokenType = {
|
|
682
|
+
NONE: 'none',
|
|
683
|
+
SOLIDUS: 'solidus',
|
|
684
|
+
PERIOD: 'period',
|
|
685
|
+
OPEN: 'open',
|
|
686
|
+
CLOSE: 'close',
|
|
687
|
+
ANNOTATION: 'annotation',
|
|
688
|
+
NUMBER: 'number',
|
|
689
|
+
SYMBOL: 'symbol'
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
// Exception class
|
|
693
|
+
class UcumException extends Error {
|
|
694
|
+
constructor(message) {
|
|
695
|
+
super(message);
|
|
696
|
+
this.name = 'UcumException';
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Utilities class
|
|
701
|
+
class Utilities {
|
|
702
|
+
static isAsciiChar(ch) {
|
|
703
|
+
const code = ch.charCodeAt(0);
|
|
704
|
+
return code >= 0 && code <= 127;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
static noString(str) {
|
|
708
|
+
return !str || !str.trim();
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
static _ensureDecimal(v) {
|
|
712
|
+
if (v == null) {
|
|
713
|
+
return null; // this is probably an error.
|
|
714
|
+
}
|
|
715
|
+
if (v instanceof Decimal) {
|
|
716
|
+
return v;
|
|
717
|
+
}
|
|
718
|
+
return new Decimal(v);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Base concept class
|
|
723
|
+
class Concept {
|
|
724
|
+
constructor(code, codeUC) {
|
|
725
|
+
this.code = code;
|
|
726
|
+
this.codeUC = codeUC;
|
|
727
|
+
this.names = [];
|
|
728
|
+
this.printSymbol = '';
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// Unit class (base for BaseUnit and DefinedUnit)
|
|
733
|
+
class Unit extends Concept {
|
|
734
|
+
constructor(code, codeUC) {
|
|
735
|
+
super(code, codeUC);
|
|
736
|
+
this.property = '';
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Base unit class
|
|
741
|
+
class BaseUnit extends Unit {
|
|
742
|
+
constructor(code, codeUC) {
|
|
743
|
+
super(code, codeUC);
|
|
744
|
+
this.kind = ConceptKind.BASEUNIT;
|
|
745
|
+
this.dim = '';
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// Defined unit class
|
|
750
|
+
class DefinedUnit extends Unit {
|
|
751
|
+
constructor(code, codeUC) {
|
|
752
|
+
super(code, codeUC);
|
|
753
|
+
this.kind = ConceptKind.UNIT;
|
|
754
|
+
this.metric = false;
|
|
755
|
+
this.isSpecial = false;
|
|
756
|
+
this.class_ = '';
|
|
757
|
+
this.value = null;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Prefix class
|
|
762
|
+
class Prefix extends Concept {
|
|
763
|
+
constructor(code, codeUC) {
|
|
764
|
+
super(code, codeUC);
|
|
765
|
+
this.kind = ConceptKind.PREFIX;
|
|
766
|
+
this.value = null;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Value class
|
|
771
|
+
class Value {
|
|
772
|
+
constructor(unit, unitUC, value) {
|
|
773
|
+
this.unit = unit || '';
|
|
774
|
+
this.unitUC = unitUC || '';
|
|
775
|
+
this.value = value;
|
|
776
|
+
this.text = '';
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// Pair class for value/unit combinations
|
|
781
|
+
class Pair {
|
|
782
|
+
constructor(value, code) {
|
|
783
|
+
this.value = value;
|
|
784
|
+
this.code = code;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
getValue() {
|
|
788
|
+
return this.value;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
getCode() {
|
|
792
|
+
return this.code;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
equals(other) {
|
|
796
|
+
if (other instanceof Pair) {
|
|
797
|
+
return this.value.equals(other.value) && this.code === other.code;
|
|
798
|
+
}
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
hashCode() {
|
|
803
|
+
return this.toString().length; // Simple hash
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
toString() {
|
|
807
|
+
return `${this.value.toString()} ${this.code}`;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Expression tree components
|
|
812
|
+
class Component {
|
|
813
|
+
// Base class for expression components
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
class Term extends Component {
|
|
817
|
+
constructor() {
|
|
818
|
+
super();
|
|
819
|
+
this.comp = null;
|
|
820
|
+
this.op = null;
|
|
821
|
+
this.term = null;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
class Symbol extends Component {
|
|
826
|
+
constructor() {
|
|
827
|
+
super();
|
|
828
|
+
this.prefix = null;
|
|
829
|
+
this.unit = null;
|
|
830
|
+
this.exponent = 1;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
class Factor extends Component {
|
|
835
|
+
constructor(value) {
|
|
836
|
+
super();
|
|
837
|
+
this.value = value;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Expression class
|
|
842
|
+
class Expression {
|
|
843
|
+
constructor() {
|
|
844
|
+
this.term = null;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Canonical unit for canonical form representation
|
|
849
|
+
class CanonicalUnit {
|
|
850
|
+
constructor(base, exponent = 1) {
|
|
851
|
+
this.base = base;
|
|
852
|
+
this.exponent = exponent;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
getBase() {
|
|
856
|
+
return this.base;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
getExponent() {
|
|
860
|
+
return this.exponent;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
setExponent(exponent) {
|
|
864
|
+
this.exponent = exponent;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// Canonical form of a unit expression
|
|
869
|
+
class Canonical {
|
|
870
|
+
constructor(value) {
|
|
871
|
+
this.value = value || new Decimal("1.000000000000000000000000000000");
|
|
872
|
+
this.units = [];
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
getValue() {
|
|
876
|
+
return this.value;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
getUnits() {
|
|
880
|
+
return this.units;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
multiplyValue(val) {
|
|
884
|
+
const v = Utilities._ensureDecimal(val);
|
|
885
|
+
this.value = this.value.multiply(v);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
divideValue(val) {
|
|
889
|
+
const v = Utilities._ensureDecimal(val)
|
|
890
|
+
this.value = this.value.divide(v);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Version details class
|
|
895
|
+
class UcumVersionDetails {
|
|
896
|
+
constructor(releaseDate, version) {
|
|
897
|
+
this.releaseDate = releaseDate;
|
|
898
|
+
this.version = version;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
getReleaseDate() {
|
|
902
|
+
return this.releaseDate;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
getVersion() {
|
|
906
|
+
return this.version;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Registry for special unit handlers
|
|
911
|
+
class Registry {
|
|
912
|
+
constructor() {
|
|
913
|
+
this.handlers = new Map();
|
|
914
|
+
|
|
915
|
+
this.register(new CelsiusHandler());
|
|
916
|
+
this.register(new FahrenheitHandler());
|
|
917
|
+
this.register(new HoldingHandler("[p'diop]", "deg"));
|
|
918
|
+
this.register(new HoldingHandler("%[slope]", "deg"));
|
|
919
|
+
this.register(new HoldingHandler("[hp_X]", "1"));
|
|
920
|
+
this.register(new HoldingHandler("[hp_C]", "1"));
|
|
921
|
+
this.register(new HoldingHandler("[pH]", "mol/l"));
|
|
922
|
+
this.register(new HoldingHandler("Np", "1"));
|
|
923
|
+
this.register(new HoldingHandler("B", "1"));
|
|
924
|
+
this.register(new HoldingHandler("B[SPL]", "10*-5.Pa", new Decimal(2)));
|
|
925
|
+
this.register(new HoldingHandler("B[V]", "V"));
|
|
926
|
+
this.register(new HoldingHandler("B[mV]", "mV"));
|
|
927
|
+
this.register(new HoldingHandler("B[uV]", "uV"));
|
|
928
|
+
this.register(new HoldingHandler("B[W]", "W"));
|
|
929
|
+
this.register(new HoldingHandler("B[kW]", "kW"));
|
|
930
|
+
this.register(new HoldingHandler("bit_s", "1"))
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
exists(code) {
|
|
934
|
+
return this.handlers.has(code);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
get(code) {
|
|
938
|
+
return this.handlers.get(code);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
register(handler) {
|
|
942
|
+
this.handlers.set(handler.code, handler);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// Special Unit Handler base class
|
|
947
|
+
class SpecialUnitHandler {
|
|
948
|
+
/**
|
|
949
|
+
* Used to connect this handler with the case sensitive unit
|
|
950
|
+
* @return {string}
|
|
951
|
+
*/
|
|
952
|
+
getCode() {
|
|
953
|
+
throw new Error("getCode() must be implemented by subclass");
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* the alternate units to convert to
|
|
958
|
+
* @return {string}
|
|
959
|
+
*/
|
|
960
|
+
getUnits() {
|
|
961
|
+
throw new Error("getUnits() must be implemented by subclass");
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* get the conversion value
|
|
966
|
+
* @return {Decimal}
|
|
967
|
+
*/
|
|
968
|
+
getValue() {
|
|
969
|
+
throw new Error("getValue() must be implemented by subclass");
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* return true if the conversion offset value != 0
|
|
974
|
+
* @return {boolean}
|
|
975
|
+
*/
|
|
976
|
+
hasOffset() {
|
|
977
|
+
throw new Error("hasOffset() must be implemented by subclass");
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* get the conversion offset value
|
|
982
|
+
* @return {Decimal}
|
|
983
|
+
* @throws {UcumException}
|
|
984
|
+
*/
|
|
985
|
+
getOffset() {
|
|
986
|
+
throw new Error("getOffset() must be implemented by subclass");
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// Celsius Handler
|
|
991
|
+
class CelsiusHandler extends SpecialUnitHandler {
|
|
992
|
+
getCode() {
|
|
993
|
+
return "Cel";
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
getUnits() {
|
|
997
|
+
return "K";
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
getValue() {
|
|
1001
|
+
return Decimal.one();
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
getOffset() {
|
|
1005
|
+
return new Decimal("-273.15", 24);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
hasOffset() {
|
|
1009
|
+
return true;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// Fahrenheit Handler
|
|
1014
|
+
class FahrenheitHandler extends SpecialUnitHandler {
|
|
1015
|
+
getCode() {
|
|
1016
|
+
return "[degF]";
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
getUnits() {
|
|
1020
|
+
return "K";
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
getValue() {
|
|
1024
|
+
try {
|
|
1025
|
+
return new Decimal("5").divide(new Decimal("9"));
|
|
1026
|
+
} catch (e) {
|
|
1027
|
+
// won't happen
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
getOffset() {
|
|
1033
|
+
return new Decimal("32", 24);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
hasOffset() {
|
|
1037
|
+
return true;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Holding Handler - generic handler for other special units
|
|
1042
|
+
class HoldingHandler extends SpecialUnitHandler {
|
|
1043
|
+
/**
|
|
1044
|
+
* @param {string} code
|
|
1045
|
+
* @param {string} units
|
|
1046
|
+
* @param {Decimal} value (optional, defaults to Decimal.one())
|
|
1047
|
+
*/
|
|
1048
|
+
constructor(code, units, value = null) {
|
|
1049
|
+
super();
|
|
1050
|
+
this.code = code;
|
|
1051
|
+
this.units = units;
|
|
1052
|
+
this.value = value || Decimal.one();
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
getCode() {
|
|
1056
|
+
return this.code;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
getUnits() {
|
|
1060
|
+
return this.units;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
getValue() {
|
|
1064
|
+
return this.value;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
getOffset() {
|
|
1068
|
+
return new Decimal("0", 24);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
hasOffset() {
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
module.exports = {
|
|
1077
|
+
ConceptKind,
|
|
1078
|
+
Operator,
|
|
1079
|
+
TokenType,
|
|
1080
|
+
UcumException,
|
|
1081
|
+
Decimal,
|
|
1082
|
+
Utilities,
|
|
1083
|
+
Concept,
|
|
1084
|
+
Unit,
|
|
1085
|
+
BaseUnit,
|
|
1086
|
+
DefinedUnit,
|
|
1087
|
+
Prefix,
|
|
1088
|
+
Value,
|
|
1089
|
+
Pair,
|
|
1090
|
+
Component,
|
|
1091
|
+
Term,
|
|
1092
|
+
Symbol,
|
|
1093
|
+
Factor,
|
|
1094
|
+
Expression,
|
|
1095
|
+
Canonical,
|
|
1096
|
+
CanonicalUnit,
|
|
1097
|
+
Registry,
|
|
1098
|
+
UcumVersionDetails
|
|
1099
|
+
};
|