cql-execution
Version:
An execution framework for the Clinical Quality Language (CQL)
374 lines • 13.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decimalOrNull = exports.decimalAdjust = exports.minValueForType = exports.minValueForInstance = exports.maxValueForType = exports.maxValueForInstance = exports.predecessor = exports.successor = exports.OverFlowException = exports.limitDecimalPrecision = exports.isValidDecimal = exports.isValidInteger = exports.overflowsOrUnderflows = exports.MAX_TIME_VALUE = exports.MIN_TIME_VALUE = exports.MAX_DATE_VALUE = exports.MIN_DATE_VALUE = exports.MAX_DATETIME_VALUE = exports.MIN_DATETIME_VALUE = exports.MIN_FLOAT_PRECISION_VALUE = exports.MIN_FLOAT_VALUE = exports.MAX_FLOAT_VALUE = exports.MIN_INT_VALUE = exports.MAX_INT_VALUE = void 0;
/* eslint-disable @typescript-eslint/no-loss-of-precision */
const exception_1 = require("../datatypes/exception");
const datetime_1 = require("../datatypes/datetime");
const uncertainty_1 = require("../datatypes/uncertainty");
exports.MAX_INT_VALUE = Math.pow(2, 31) - 1;
exports.MIN_INT_VALUE = Math.pow(-2, 31);
exports.MAX_FLOAT_VALUE = 99999999999999999999.99999999;
exports.MIN_FLOAT_VALUE = -99999999999999999999.99999999;
exports.MIN_FLOAT_PRECISION_VALUE = Math.pow(10, -8);
exports.MIN_DATETIME_VALUE = datetime_1.MIN_DATETIME_VALUE;
exports.MAX_DATETIME_VALUE = datetime_1.MAX_DATETIME_VALUE;
exports.MIN_DATE_VALUE = datetime_1.MIN_DATE_VALUE;
exports.MAX_DATE_VALUE = datetime_1.MAX_DATE_VALUE;
exports.MIN_TIME_VALUE = datetime_1.MIN_TIME_VALUE;
exports.MAX_TIME_VALUE = datetime_1.MAX_TIME_VALUE;
function overflowsOrUnderflows(value) {
if (value == null) {
return false;
}
if (value.isQuantity) {
if (!isValidDecimal(value.value)) {
return true;
}
}
else if (value.isTime && value.isTime()) {
if (value.after(exports.MAX_TIME_VALUE)) {
return true;
}
if (value.before(exports.MIN_TIME_VALUE)) {
return true;
}
}
else if (value.isDateTime) {
if (value.after(exports.MAX_DATETIME_VALUE)) {
return true;
}
if (value.before(exports.MIN_DATETIME_VALUE)) {
return true;
}
}
else if (value.isDate) {
if (value.after(exports.MAX_DATE_VALUE)) {
return true;
}
if (value.before(exports.MIN_DATE_VALUE)) {
return true;
}
}
else if (Number.isInteger(value)) {
if (!isValidInteger(value)) {
return true;
}
}
else if (value.isUncertainty) {
return overflowsOrUnderflows(value.low) || overflowsOrUnderflows(value.high);
}
else {
if (!isValidDecimal(value)) {
return true;
}
}
return false;
}
exports.overflowsOrUnderflows = overflowsOrUnderflows;
function isValidInteger(integer) {
if (isNaN(integer)) {
return false;
}
if (integer > exports.MAX_INT_VALUE) {
return false;
}
if (integer < exports.MIN_INT_VALUE) {
return false;
}
return true;
}
exports.isValidInteger = isValidInteger;
function isValidDecimal(decimal) {
if (isNaN(decimal)) {
return false;
}
if (decimal > exports.MAX_FLOAT_VALUE) {
return false;
}
if (decimal < exports.MIN_FLOAT_VALUE) {
return false;
}
return true;
}
exports.isValidDecimal = isValidDecimal;
function limitDecimalPrecision(decimal) {
let decimalString = decimal.toString();
// For decimals so large that they are represented in scientific notation, javascript has already limited
// the decimal to its own constraints, so we can't determine the original precision. Leave as-is unless
// this becomes problematic, in which case we would need our own parseFloat.
if (decimalString.indexOf('e') !== -1) {
return decimal;
}
const splitDecimalString = decimalString.split('.');
const decimalPoints = splitDecimalString[1];
if (decimalPoints != null && decimalPoints.length > 8) {
decimalString = splitDecimalString[0] + '.' + splitDecimalString[1].substring(0, 8);
}
return parseFloat(decimalString);
}
exports.limitDecimalPrecision = limitDecimalPrecision;
class OverFlowException extends exception_1.Exception {
}
exports.OverFlowException = OverFlowException;
function successor(val) {
if (typeof val === 'number') {
if (Number.isInteger(val)) {
if (val >= exports.MAX_INT_VALUE) {
throw new OverFlowException();
}
else {
return val + 1;
}
}
else {
if (val >= exports.MAX_FLOAT_VALUE) {
throw new OverFlowException();
}
else {
return val + exports.MIN_FLOAT_PRECISION_VALUE;
}
}
}
else if (val && val.isTime && val.isTime()) {
if (val.sameAs(exports.MAX_TIME_VALUE)) {
throw new OverFlowException();
}
else {
return val.successor();
}
}
else if (val && val.isDateTime) {
if (val.sameAs(exports.MAX_DATETIME_VALUE)) {
throw new OverFlowException();
}
else {
return val.successor();
}
}
else if (val && val.isDate) {
if (val.sameAs(exports.MAX_DATE_VALUE)) {
throw new OverFlowException();
}
else {
return val.successor();
}
}
else if (val && val.isUncertainty) {
// For uncertainties, if the high is the max val, don't increment it
const high = (() => {
try {
return successor(val.high);
}
catch (e) {
return val.high;
}
})();
return new uncertainty_1.Uncertainty(successor(val.low), high);
}
else if (val && val.isQuantity) {
const succ = val.clone();
succ.value = successor(val.value);
return succ;
}
else if (val == null) {
return null;
}
}
exports.successor = successor;
function predecessor(val) {
if (typeof val === 'number') {
if (Number.isInteger(val)) {
if (val <= exports.MIN_INT_VALUE) {
throw new OverFlowException();
}
else {
return val - 1;
}
}
else {
if (val <= exports.MIN_FLOAT_VALUE) {
throw new OverFlowException();
}
else {
return val - exports.MIN_FLOAT_PRECISION_VALUE;
}
}
}
else if (val && val.isTime && val.isTime()) {
if (val.sameAs(exports.MIN_TIME_VALUE)) {
throw new OverFlowException();
}
else {
return val.predecessor();
}
}
else if (val && val.isDateTime) {
if (val.sameAs(exports.MIN_DATETIME_VALUE)) {
throw new OverFlowException();
}
else {
return val.predecessor();
}
}
else if (val && val.isDate) {
if (val.sameAs(exports.MIN_DATE_VALUE)) {
throw new OverFlowException();
}
else {
return val.predecessor();
}
}
else if (val && val.isUncertainty) {
// For uncertainties, if the low is the min val, don't decrement it
const low = (() => {
try {
return predecessor(val.low);
}
catch (e) {
return val.low;
}
})();
return new uncertainty_1.Uncertainty(low, predecessor(val.high));
}
else if (val && val.isQuantity) {
const pred = val.clone();
pred.value = predecessor(val.value);
return pred;
}
else if (val == null) {
return null;
}
}
exports.predecessor = predecessor;
function maxValueForInstance(val) {
if (typeof val === 'number') {
if (Number.isInteger(val)) {
return exports.MAX_INT_VALUE;
}
else {
return exports.MAX_FLOAT_VALUE;
}
}
else if (val && val.isTime && val.isTime()) {
return exports.MAX_TIME_VALUE === null || exports.MAX_TIME_VALUE === void 0 ? void 0 : exports.MAX_TIME_VALUE.copy();
}
else if (val && val.isDateTime) {
return exports.MAX_DATETIME_VALUE === null || exports.MAX_DATETIME_VALUE === void 0 ? void 0 : exports.MAX_DATETIME_VALUE.copy();
}
else if (val && val.isDate) {
return exports.MAX_DATE_VALUE === null || exports.MAX_DATE_VALUE === void 0 ? void 0 : exports.MAX_DATE_VALUE.copy();
}
else if (val && val.isQuantity) {
const val2 = val.clone();
val2.value = maxValueForInstance(val2.value);
return val2;
}
else {
return null;
}
}
exports.maxValueForInstance = maxValueForInstance;
function maxValueForType(type, quantityInstance) {
switch (type) {
case '{urn:hl7-org:elm-types:r1}Integer':
return exports.MAX_INT_VALUE;
case '{urn:hl7-org:elm-types:r1}Decimal':
return exports.MAX_FLOAT_VALUE;
case '{urn:hl7-org:elm-types:r1}DateTime':
return exports.MAX_DATETIME_VALUE === null || exports.MAX_DATETIME_VALUE === void 0 ? void 0 : exports.MAX_DATETIME_VALUE.copy();
case '{urn:hl7-org:elm-types:r1}Date':
return exports.MAX_DATE_VALUE === null || exports.MAX_DATE_VALUE === void 0 ? void 0 : exports.MAX_DATE_VALUE.copy();
case '{urn:hl7-org:elm-types:r1}Time':
return exports.MAX_TIME_VALUE === null || exports.MAX_TIME_VALUE === void 0 ? void 0 : exports.MAX_TIME_VALUE.copy();
case '{urn:hl7-org:elm-types:r1}Quantity': {
if (quantityInstance == null) {
// can't infer a quantity unit type from nothing]
return null;
}
const maxQty = quantityInstance.clone();
maxQty.value = maxValueForInstance(maxQty.value);
return maxQty;
}
}
return null;
}
exports.maxValueForType = maxValueForType;
function minValueForInstance(val) {
if (typeof val === 'number') {
if (Number.isInteger(val)) {
return exports.MIN_INT_VALUE;
}
else {
return exports.MIN_FLOAT_VALUE;
}
}
else if (val && val.isTime && val.isTime()) {
return exports.MIN_TIME_VALUE === null || exports.MIN_TIME_VALUE === void 0 ? void 0 : exports.MIN_TIME_VALUE.copy();
}
else if (val && val.isDateTime) {
return exports.MIN_DATETIME_VALUE === null || exports.MIN_DATETIME_VALUE === void 0 ? void 0 : exports.MIN_DATETIME_VALUE.copy();
}
else if (val && val.isDate) {
return exports.MIN_DATE_VALUE === null || exports.MIN_DATE_VALUE === void 0 ? void 0 : exports.MIN_DATE_VALUE.copy();
}
else if (val && val.isQuantity) {
const val2 = val.clone();
val2.value = minValueForInstance(val2.value);
return val2;
}
else {
return null;
}
}
exports.minValueForInstance = minValueForInstance;
function minValueForType(type, quantityInstance) {
switch (type) {
case '{urn:hl7-org:elm-types:r1}Integer':
return exports.MIN_INT_VALUE;
case '{urn:hl7-org:elm-types:r1}Decimal':
return exports.MIN_FLOAT_VALUE;
case '{urn:hl7-org:elm-types:r1}DateTime':
return exports.MIN_DATETIME_VALUE === null || exports.MIN_DATETIME_VALUE === void 0 ? void 0 : exports.MIN_DATETIME_VALUE.copy();
case '{urn:hl7-org:elm-types:r1}Date':
return exports.MIN_DATE_VALUE === null || exports.MIN_DATE_VALUE === void 0 ? void 0 : exports.MIN_DATE_VALUE.copy();
case '{urn:hl7-org:elm-types:r1}Time':
return exports.MIN_TIME_VALUE === null || exports.MIN_TIME_VALUE === void 0 ? void 0 : exports.MIN_TIME_VALUE.copy();
case '{urn:hl7-org:elm-types:r1}Quantity': {
if (quantityInstance == null) {
// can't infer a quantity unit type from nothing]
return null;
}
const minQty = quantityInstance.clone();
minQty.value = minValueForInstance(minQty.value);
return minQty;
}
}
return null;
}
exports.minValueForType = minValueForType;
function decimalAdjust(type, value, exp) {
//If the exp is undefined or zero...
if (typeof exp === 'undefined' || +exp === 0) {
return Math[type](value);
}
value = +value;
exp = +exp;
//If the value is not a number or the exp is not an integer...
if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
return NaN;
}
//Shift
value = value.toString().split('e');
let v = value[1] ? +value[1] - exp : -exp;
value = Math[type](+(value[0] + 'e' + v));
//Shift back
value = value.toString().split('e');
v = value[1] ? +value[1] + exp : exp;
return +(value[0] + 'e' + v);
}
exports.decimalAdjust = decimalAdjust;
function decimalOrNull(value) {
return isValidDecimal(value) ? value : null;
}
exports.decimalOrNull = decimalOrNull;
//# sourceMappingURL=math.js.map