Skip to content

Commit

Permalink
Merge pull request #12 from Virnkord/math
Browse files Browse the repository at this point in the history
Math
  • Loading branch information
plynchnlm authored May 28, 2019
2 parents ce8e243 + 402cf8d commit e8c0d9b
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
This log documents significant changes for each release. This project follows
[Semantic Versioning](http://semver.org/).

## [0.14.0] - 2019-05-28
### Added
- Math functions defined in paragraphs 5.7.1 - 5.7.4 and 5.7.7 - 5.7.10 of the FHIRPath specification.

## [0.13.0] - 2019-05-21
### Added
- Functions ln() and log() in 5.7 (Math) of the FHIRPath specification.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fhirpath",
"version": "0.13.0",
"version": "0.14.0",
"description": "A FHIRPath engine",
"main": "src/fhirpath.js",
"dependencies": {
Expand Down
8 changes: 8 additions & 0 deletions src/fhirpath.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,16 @@ engine.invocationTable = {
replaceMatches: {fn: strings.replaceMatches, arity: {2: ["String", "String"]}},
length: {fn: strings.length },

abs: {fn: math.abs},
ceiling: {fn: math.ceiling},
exp: {fn: math.exp},
floor: {fn: math.floor},
ln: {fn: math.ln},
log: {fn: math.log, arity: {1: ["Number"]}, nullable: true},
power: {fn: math.power, arity: {1: ["Number"]}, nullable: true},
round: {fn: math.round, arity: {1: ["Number"]}},
sqrt: {fn: math.sqrt},
truncate: {fn: math.truncate},

now: {fn: datetime.now },
today: {fn: datetime.today },
Expand Down
89 changes: 88 additions & 1 deletion src/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var engine = {};

function ensureNumberSingleton(x){
if (typeof x != 'number'){
if (x.length == 1){
if (x.length == 1 && typeof x[0] == 'number'){
return x[0];
}else{
throw new Error("Expected number, but got " + JSON.stringify(x));
Expand Down Expand Up @@ -65,6 +65,42 @@ engine.mod = function(x, y){
return x % y;
};

engine.abs = function(x){
if (isEmpty(x)){
return [];
}else{
let num = ensureNumberSingleton(x);
return Math.abs(num);
}
};

engine.ceiling = function(x){
if (isEmpty(x)){
return [];
}else{
let num = ensureNumberSingleton(x);
return Math.ceil(num);
}
};

engine.exp = function(x){
if (isEmpty(x)){
return [];
}else{
let num = ensureNumberSingleton(x);
return Math.exp(num);
}
};

engine.floor = function(x){
if (isEmpty(x)){
return [];
}else{
let num = ensureNumberSingleton(x);
return Math.floor(num);
}
};

engine.ln = function(x){
if (isEmpty(x)){
return [];
Expand All @@ -84,4 +120,55 @@ engine.log = function(x, base){
}
};

engine.power = function(x, degree){
if (isEmpty(x) || isEmpty(degree)){
return [];
}else{
let num = ensureNumberSingleton(x);
let num2 = ensureNumberSingleton(degree);
if (num < 0 && (Math.floor(num2) != num2)){
return [];
}else{
return Math.pow(num, num2);
}
}
};

engine.round = function(x, acc){
if (isEmpty(x)){
return [];
}else{
let num = ensureNumberSingleton(x);
if (isEmpty(acc)){
return (Math.round(num));
}else{
let num2 = ensureNumberSingleton(acc);
let degree = Math.pow(10, num2);
return (Math.round(num * degree) / degree);
}
}
};

engine.sqrt = function(x){
if (isEmpty(x)){
return [];
}else{
if (x < 0){
return [];
}else{
let num = ensureNumberSingleton(x);
return Math.sqrt(num);
}
}
};

engine.truncate = function(x){
if (isEmpty(x)){
return [];
}else{
let num = ensureNumberSingleton(x);
return Math.trunc(num);
}
};

module.exports = engine;
152 changes: 151 additions & 1 deletion test/cases/5.7_math.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,77 @@
tests:
- desc: '5.7 Math'

- desc: '5.7.1 abs() : Integer | Decimal | Quantity'
- desc: '** Can take the absolute value of a number'
expression: Math.d2.abs()
result: [1.1]
# - desc: '** Can return a number equal to the input if input value is a quantity'
# expression: Math.quan.abs()
# result: [4.5 'mg']
- desc: '** Empty result when taking the absolute value of empty collection'
expression: Math.n2.abs()
result: []
- desc: '** Error taking the absolute value due to too many input parameters'
expression: Math.n3.abs(n4)
error: true
- desc: '** Error taking the absolute value if the input collection contains multiple items'
expression: Math.arr.abs()
error: true

- desc: '5.7.2 ceiling() : Integer'
- desc: '** Can return a number equal to the input'
expression: Math.n1.ceiling()
result: [1]
- desc: '** Can round a number upward to its nearest integer'
expression: Math.d1.ceiling()
result: [2]
- desc: '** Can round a number upward to its nearest integer'
expression: Math.d2.ceiling()
result: [-1]
- desc: '** Empty result when rounding a number upward to its nearest integer from empty collection'
expression: Math.n2.ceiling()
result: []
- desc: '** Error rounding a number due to too many input parameters'
expression: Math.n3.ceiling(n4)
error: true
- desc: '** Error rounding a number if the input collection contains multiple items'
expression: Math.arr.ceiling()
error: true

- desc: '5.7.3 exp() : Decimal'
- desc: '** Can raise e to the input number power'
expression: Math.n0.exp()
result: [1]
- desc: '** Empty result for empty degree'
expression: Math.n2.exp()
result: []
- desc: '** Error exponentiation due to too many input parameters'
expression: Math.n3.exp(n4)
error: true
- desc: '** Error exponentiation if the input collection contains multiple items'
expression: Math.arr.exp()
error: true

- desc: '5.7.4 floor() : Integer'
- desc: '** Can return a number equal to the input'
expression: Math.n1.floor()
result: [1]
- desc: '** Can round a number downward to its nearest integer'
expression: Math.d1.floor()
result: [1]
- desc: '** Can round a number downward to its nearest integer'
expression: Math.d2.floor()
result: [-2]
- desc: '** Empty result when rounding a number downward to its nearest integer from empty collection'
expression: Math.n2.floor()
result: []
- desc: '** Error rounding a number due to too many input parameters'
expression: Math.n3.floor(n4)
error: true
- desc: '** Error rounding a number if the input collection contains multiple items'
expression: Math.arr.floor()
error: true

- desc: '5.7.5 ln() : Decimal'
- desc: '** Can take the natural logarithm of the number'
expression: Math.n1.ln()
Expand Down Expand Up @@ -31,11 +103,89 @@ tests:
expression: Math.n3.log([3, 5])
error: true

- desc: '5.7.7 power(exponent : Integer | Decimal) : Integer | Decimal'
- desc: '** Can raise input number to the power of given degree'
expression: Math.n4.power(2)
result: [64]
- desc: '** Empty result if the power cannot be represented'
expression: n6.power(1.5)
result: []
- desc: '** Empty result if the power cannot be represented'
expression: Math.n6.power(0.5)
result: []
- desc: '** Empty result when raising empty collection to a power'
expression: Math.n2.power(8)
result: []
- desc: '** Empty result when raising collection to the empty power'
expression: Math.n3.power(n2)
result: []
- desc: '** Error raising to a power if the input collection contains multiple items'
expression: Math.arr.power(8)
error: true
- desc: '** Error raising to a power due to too many input parameters'
expression: Math.n3.power([3, 5])
error: true

- desc: '5.7.8 round([precision : Integer]) : Decimal'
- desc: '** Can round number with a given accuracy'
expression: Math.d3.round(2)
result: [13.85]
- desc: '** Can round a number to the nearest integer if a given accuracy is empty'
expression: Math.d2.round(n2)
result: [-1]
- desc: '** Empty result when rounding empty number'
expression: Math.n2.round(n3)
result: []
- desc: '** Error rounding if the input collection contains multiple items'
expression: Math.arr.round(8)
error: true
- desc: '** Error rounding due to too many input parameters'
expression: Math.n3.round([3, 5])
error: true

- desc: '5.7.9 sqrt() : Decimal'
- desc: '** Can take square root'
expression: Math.n5.sqrt()
result: [4]
- desc: '** Empty result when taking square root of a negative number'
expression: Math.d2.sqrt()
result: []
- desc: '** Empty result when taking square root of an empty collection'
expression: Math.n2.sqrt()
result: []
- desc: '** Error taking square root due to too many input parameters'
expression: Math.n3.sqrt(n4)
error: true
- desc: '** Error taking square root if the input collection contains multiple items'
expression: Math.arr.sqrt()
error: true

- desc: '5.7.10 truncate() : Integer'
- desc: '** Can return the integer part of the number'
expression: Math.d1.truncate()
result: [1]
- desc: '** Empty result when taking integer part from empty collection'
expression: Math.n2.truncate()
result: []
- desc: '** Error taking integer part due to too many input parameters'
expression: Math.n3.truncate(n4)
error: true
- desc: '** Error taking integer part if the input collection contains multiple items'
expression: Math.arr.truncate()
error: true

subject:
resourceType: Math
n0: 0
n1: 1
n2: []
n3: 2
n4: 8
n5: 16
n6: -1
d1: 1.1
d2: -1.1
d3: 13.84512
arr: [3, 5]
t: true
quan: [4.5 'mg']

0 comments on commit e8c0d9b

Please sign in to comment.