Skip to content

Commit 014d387

Browse files
committed
CQL Decimal improvements, checkpoint 3
1 parent 3e97393 commit 014d387

22 files changed

Lines changed: 350 additions & 261 deletions

src/datatypes/decimal.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import { Decimal as DecimalJS } from 'decimal.js';
32

43
// Default precision is set to 30 significant figures. (Not decimal places)
@@ -44,28 +43,25 @@ export class Decimal {
4443
return this.setScale(CQL_IMPLICIT_SCALE, CQL_IMPLICIT_ROUNDING);
4544
}
4645

47-
private applyWrapper(
48-
operation: (value: any) => DecimalJS,
49-
other: DecimalInput
50-
): Decimal {
46+
private applyWrapper(operation: (value: any) => DecimalJS, other: DecimalInput): Decimal {
5147
const operand = other instanceof Decimal ? other.value : other;
5248

5349
return new Decimal(operation.call(this.value, operand));
5450
}
5551

56-
add(other: DecimalInput) : Decimal {
52+
add(other: DecimalInput): Decimal {
5753
return this.applyWrapper(this.value.add, other);
5854
}
5955

60-
subtract(other: DecimalInput) : Decimal {
56+
subtract(other: DecimalInput): Decimal {
6157
return this.applyWrapper(this.value.minus, other);
6258
}
6359

64-
multiplyBy(other: DecimalInput) : Decimal {
60+
multiplyBy(other: DecimalInput): Decimal {
6561
return this.applyWrapper(this.value.times, other);
6662
}
6763

68-
divideBy(other: DecimalInput) : Decimal {
64+
divideBy(other: DecimalInput): Decimal {
6965
if (toNumber(other) === 0) {
7066
throw new RangeError('Cannot divide a decimal by zero');
7167
}
@@ -82,7 +78,7 @@ export class Decimal {
8278

8379
compareTo(other: DecimalInput) {
8480
if (other instanceof Decimal) {
85-
return this.value.comparedTo(other.value)
81+
return this.value.comparedTo(other.value);
8682
}
8783
return this.value.comparedTo(other);
8884
}
@@ -94,7 +90,7 @@ export class Decimal {
9490
greaterThanOrEquals(other: DecimalInput) {
9591
return this.compareTo(other) >= 0;
9692
}
97-
93+
9894
lessThan(other: DecimalInput) {
9995
return this.compareTo(other) < 0;
10096
}
@@ -123,19 +119,19 @@ export class Decimal {
123119
return new Decimal(this.value.abs());
124120
}
125121

126-
truncate() : number {
122+
truncate(): number {
127123
return this.value.truncated().toNumber();
128124
}
129125

130-
truncated() : Decimal {
126+
truncated(): Decimal {
131127
return new Decimal(this.value.truncated());
132128
}
133129

134-
ceil() : number {
130+
ceil(): number {
135131
return this.value.ceil().toNumber();
136132
}
137133

138-
floor() : number {
134+
floor(): number {
139135
return this.value.floor().toNumber();
140136
}
141137

@@ -177,7 +173,7 @@ export class Decimal {
177173
if (!Number.isInteger(scale) || scale < 0) {
178174
throw new RangeError('Decimal scale must be a non-negative integer');
179175
}
180-
176+
181177
return new Decimal(this.value.toDecimalPlaces(scale, roundingMode));
182178
}
183179

@@ -203,8 +199,8 @@ export class Decimal {
203199
}
204200
}
205201

206-
export const MAX_DECIMAL_STRING = "99999999999999999999.99999999";
207-
export const MIN_DECIMAL_STRING = "-99999999999999999999.99999999";
202+
export const MAX_DECIMAL_STRING = '99999999999999999999.99999999';
203+
export const MIN_DECIMAL_STRING = '-99999999999999999999.99999999';
208204

209205
export const MAX_DECIMAL_VALUE = Decimal.from(MAX_DECIMAL_STRING);
210206
export const MIN_DECIMAL_VALUE = Decimal.from(MIN_DECIMAL_STRING);

src/datatypes/interval.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
ELM_QUANTITY_TYPE,
2121
ELM_ANY_TYPE
2222
} from '../util/elmTypes';
23-
import { MIN_FLOAT_VALUE } from '../util/limits';
2423
import { Quantity } from './quantity';
2524
import { Decimal, MIN_DECIMAL_VALUE } from './decimal';
2625

@@ -782,8 +781,8 @@ export class Interval {
782781
toString() {
783782
const start = this.lowClosed ? '[' : '(';
784783
const end = this.highClosed ? ']' : ')';
785-
const lowString = this.low == null ? "null" : this.low.toString();
786-
const highString = this.high == null ? "null" : this.high.toString();
784+
const lowString = this.low == null ? 'null' : this.low.toString();
785+
const highString = this.high == null ? 'null' : this.high.toString();
787786
return start + lowString + ', ' + highString + end;
788787
}
789788
}

src/datatypes/quantity.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ELM_DECIMAL_TYPE } from '../util/elmTypes';
2-
import { decimalAdjust, add, subtract, isValidDecimal, overflowsOrUnderflows } from '../util/math';
2+
import { add, subtract, isValidDecimal, overflowsOrUnderflows } from '../util/math';
33
import { Decimal } from './decimal';
44
import {
55
checkUnit,
@@ -16,7 +16,7 @@ export class Quantity {
1616
value?: Decimal | string | number | bigint,
1717
public unit?: any
1818
) {
19-
if (value == null || typeof value === 'number' && isNaN(value)) {
19+
if (value == null || (typeof value === 'number' && isNaN(value))) {
2020
throw new Error('Cannot create a quantity with an undefined value');
2121
}
2222
this.value = Decimal.from(value).normalized();
@@ -114,7 +114,11 @@ export class Quantity {
114114
}
115115

116116
dividedBy(other: any) {
117-
if (other == null || other === 0 || (other.value != null && Decimal.from(other.value).equals(0))) {
117+
if (
118+
other == null ||
119+
other === 0 ||
120+
(other.value != null && Decimal.from(other.value).equals(0))
121+
) {
118122
return null;
119123
} else if (!other.isQuantity) {
120124
// convert it to a quantity w/ unit 1

src/datatypes/uncertainty.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export class Uncertainty {
143143

144144
if (typeof a.before === 'function') {
145145
return a.before(b, precision);
146-
} else if (a.isDecimal) {
146+
} else if (a.isDecimal) {
147147
return a.lessThan(b);
148148
} else {
149149
return a < b;

src/elm/aggregate.ts

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Context } from '../runtime/context';
66
import { Exception } from '../datatypes/exception';
77
import { greaterThan, lessThan } from '../util/comparison';
88
import { build } from './builder';
9-
import { overflowsOrUnderflows } from '../util/math';
9+
import { overflowsOrUnderflows, finalizeNumericResult } from '../util/math';
1010
import { ELM_DECIMAL_TYPE } from '../util/elmTypes';
1111

1212
class AggregateExpression extends Expression {
@@ -18,28 +18,6 @@ class AggregateExpression extends Expression {
1818
}
1919
}
2020

21-
function hasDecimals(values: any[]) {
22-
return values.some(value => value && value.isDecimal);
23-
}
24-
25-
function isDecimal(value: any): value is Decimal {
26-
return value != null && value.isDecimal;
27-
}
28-
29-
function sumDecimals(values: Decimal[]) {
30-
return values.reduce((sum, value) => sum.add(value));
31-
}
32-
33-
function productDecimals(values: Decimal[]) {
34-
return values.reduce((product, value) => product.multiplyBy(value));
35-
}
36-
37-
function decimalResult(value: number, values: any[], resultTypeName?: string) {
38-
return hasDecimals(values) || resultTypeName === ELM_DECIMAL_TYPE
39-
? Decimal.from(value).normalized()
40-
: value;
41-
}
42-
4321
export class Count extends AggregateExpression {
4422
constructor(json: any) {
4523
super(json);
@@ -76,12 +54,16 @@ export class Sum extends AggregateExpression {
7654
}
7755

7856
if (hasOnlyQuantities(items)) {
79-
const sum = sumDecimals(getValuesFromQuantities(items));
57+
const sum = sumOfDecimals(getValuesFromQuantities(items));
8058
return overflowsOrUnderflows(sum, ELM_DECIMAL_TYPE) ? null : new Quantity(sum, items[0].unit);
8159
} else {
82-
const sum = hasDecimals(items)
83-
? sumDecimals(items.map(Decimal.from))
84-
: items.reduce((x: any, y: any) => x + y);
60+
let sum;
61+
if (hasDecimals(items)) {
62+
sum = sumOfDecimals(items.map(Decimal.from));
63+
} else {
64+
sum = items.reduce((x: any, y: any) => x + y);
65+
}
66+
sum = finalizeNumericResult(sum);
8567
return overflowsOrUnderflows(sum, this.resultTypeName) ? null : sum;
8668
}
8769
}
@@ -177,11 +159,11 @@ export class Avg extends AggregateExpression {
177159
}
178160

179161
if (hasOnlyQuantities(items)) {
180-
const sum = sumDecimals(getValuesFromQuantities(items));
162+
const sum = sumOfDecimals(getValuesFromQuantities(items));
181163
return new Quantity(sum.divideBy(items.length), items[0].unit);
182164
} else {
183165
// return type is always Decimal, so just map everything to Decimals
184-
return sumDecimals(items.map(Decimal.from)).divideBy(items.length).normalized();
166+
return sumOfDecimals(items.map(Decimal.from)).divideBy(items.length).normalized();
185167
}
186168
}
187169
}
@@ -206,14 +188,17 @@ export class Median extends AggregateExpression {
206188
return null;
207189
}
208190

209-
if (!hasOnlyQuantities(items)) {
210-
return hasDecimals(items)
211-
? medianOfDecimals(items.map(Decimal.from))
212-
: decimalResult(medianOfNumbers(items), items, this.resultTypeName);
191+
if (hasOnlyQuantities(items)) {
192+
const median = medianOfDecimals(getValuesFromQuantities(items));
193+
return new Quantity(median, items[0].unit);
194+
}
195+
196+
if (hasDecimals(items)) {
197+
const decimals = items.map(Decimal.from);
198+
return finalizeNumericResult(medianOfDecimals(decimals));
213199
}
214200

215-
const median = medianOfDecimals(getValuesFromQuantities(items));
216-
return new Quantity(median, items[0].unit);
201+
return medianOfNumbers(items);
217202
}
218203
}
219204

@@ -240,7 +225,7 @@ export class Mode extends AggregateExpression {
240225

241226
if (hasOnlyQuantities(filtered)) {
242227
const values = getValuesFromQuantities(filtered);
243-
let mode = this.mode(values);
228+
const mode = this.mode(values);
244229
if (mode.length === 1) {
245230
return new Quantity(mode[0], items[0].unit);
246231
} else {
@@ -362,16 +347,19 @@ export class Product extends AggregateExpression {
362347
}
363348

364349
if (hasOnlyQuantities(items)) {
365-
const product = productDecimals(getValuesFromQuantities(items));
350+
const product = productOfDecimals(getValuesFromQuantities(items));
366351
// Units are not multiplied for the geometric product
367352
return overflowsOrUnderflows(product, ELM_DECIMAL_TYPE)
368353
? null
369354
: new Quantity(product, items[0].unit);
370355
} else {
371-
const product = hasDecimals(items)
372-
? productDecimals(items.map(Decimal.from))
373-
: items.reduce((x: number, y: number) => x * y);
374-
const result = isDecimal(product) ? product : decimalResult(product, items, this.resultTypeName);
356+
let result;
357+
if (hasDecimals(items)) {
358+
result = productOfDecimals(items.map(Decimal.from));
359+
} else {
360+
result = items.reduce((x: number, y: number) => x * y);
361+
}
362+
result = finalizeNumericResult(result);
375363
return overflowsOrUnderflows(result, this.resultTypeName) ? null : result;
376364
}
377365
}
@@ -399,12 +387,13 @@ export class GeometricMean extends AggregateExpression {
399387
}
400388

401389
if (hasOnlyQuantities(items)) {
402-
const product = productDecimals(getValuesFromQuantities(items));
390+
const product = productOfDecimals(getValuesFromQuantities(items));
403391
const geoMean = product.power(1.0 / items.length);
404392
return new Quantity(geoMean, items[0].unit);
405393
} else {
406-
return productDecimals(items.map(Decimal.from))
407-
.power(1.0 / items.length).normalized();
394+
return productOfDecimals(items.map(Decimal.from))
395+
.power(1.0 / items.length)
396+
.normalized();
408397
}
409398
}
410399
}
@@ -458,6 +447,10 @@ export class AnyTrue extends AggregateExpression {
458447
}
459448
}
460449

450+
function hasDecimals(values: any[]) {
451+
return values.some(value => value && value.isDecimal);
452+
}
453+
461454
function processQuantities(values: any[]) {
462455
const items = removeNulls(values);
463456
if (hasOnlyQuantities(items)) {
@@ -502,7 +495,13 @@ function medianOfNumbers(numbers: number[]) {
502495
function medianOfDecimals(decimals: Decimal[]) {
503496
const items = [...decimals].sort((a, b) => a.compareTo(b));
504497
const middle = Math.floor(items.length / 2);
505-
return items.length % 2 === 1
506-
? items[middle]
507-
: items[middle - 1].add(items[middle]).divideBy(2);
498+
return items.length % 2 === 1 ? items[middle] : items[middle - 1].add(items[middle]).divideBy(2);
499+
}
500+
501+
function sumOfDecimals(values: Decimal[]) {
502+
return values.reduce((sum, value) => sum.add(value));
503+
}
504+
505+
function productOfDecimals(values: Decimal[]) {
506+
return values.reduce((product, value) => product.multiplyBy(value));
508507
}

0 commit comments

Comments
 (0)