clqms-fe1/docs/test-calc-engine.md

7.3 KiB

Calculator Service Operators Reference

Overview

The CalculatorService (app/Services/CalculatorService.php) evaluates formulas with Symfony's ExpressionLanguage. This document lists the operators, functions, and constants that are available in the current implementation.


API Endpoints

All endpoints live under /api and accept JSON. Responses use the standard { status, message, data } envelope unless stated otherwise.

Calculate By Test Site

Uses the testdefcal definition for a test site. The incoming body supplies the variables required by the formula.

POST /api/calc/testsite/123
Content-Type: application/json

{
  "result": 85,
  "gender": "female",
  "age": 30
}

Response:

{
  "status": "success",
  "data": {
    "result": 92.4,
    "testSiteID": 123,
    "formula": "{result} * {factor} + {age}",
    "variables": {
      "result": 85,
      "gender": "female",
      "age": 30
    }
  }
}

Calculate By Code Or Name

Evaluates a configured calculation by TestSiteCode or TestSiteName. Returns a compact map with a single key/value or {} on failure.

POST /api/calc/testcode/GLU
Content-Type: application/json

{
  "result": 110,
  "factor": 1.1
}

Response:

{
  "GLU": 121
}

Supported Operators

Arithmetic Operators

Operator Description Example Result
+ Addition 5 + 3 8
- Subtraction 10 - 4 6
* Multiplication 6 * 7 42
/ Division 20 / 4 5
% Modulo 20 % 6 2
** Exponentiation (power) 2 ** 3 8

Comparison Operators

Operator Description Example
== Equal {result} == 10
!= Not equal {result} != 10
< Less than {result} < 10
<= Less than or equal {result} <= 10
> Greater than {result} > 10
>= Greater than or equal {result} >= 10

Logical Operators

Operator Description Example
and / && Logical AND {result} > 0 and {factor} > 0
or / ` `
! / not Logical NOT not ({result} > 0)

Conditional Operators

Operator Description Example
?: Ternary {result} > 10 ? {result} : 10
?? Null coalescing {result} ?? 0

Parentheses

Use parentheses to control operation precedence:

(2 + 3) * 4   // Result: 20
2 + 3 * 4     // Result: 14

Notes

  • ^ is bitwise XOR (not exponentiation). Use ** for powers.
  • Variables must be numeric after normalization (gender is mapped to 0/1/2).

Functions

Only the default ExpressionLanguage functions are available:

Function Description Example
min(a, b, ...) Minimum value min({result}, 10)
max(a, b, ...) Maximum value max({result}, 10)
constant(name) PHP constant by name constant("PHP_INT_MAX")
enum(name) PHP enum case by name enum("App\\Enum\\Status::Active")

Constants

ExpressionLanguage recognizes boolean and null literals:

Constant Value Description
true true Boolean true
false false Boolean false
null null Null value

Variables in CalculatorService

When using calculateFromDefinition(), the following variables are automatically available:

Variable Description Type
{result} The test result value Float
{factor} Calculation factor (default: 1) Float
{gender} Gender value (0=Unknown, 1=Female, 2=Male) Integer
{age} Patient age Float
{ref_low} Reference range low value Float
{ref_high} Reference range high value Float

Gender Mapping

The gender variable accepts the following values:

Value Description
0 Unknown
1 Female
2 Male

Or use string values: 'unknown', 'female', 'male'


Implicit Multiplication

Implicit multiplication is not supported. Always use * between values:

Expression Use Instead
2x 2 * x
{result}{factor} {result} * {factor}

Usage Examples

Basic Calculation

use App\Services\CalculatorService;

$calculator = new CalculatorService();

// Simple arithmetic
$result = $calculator->calculate("5 + 3 * 2");
// Result: 11

// Using min/max
$result = $calculator->calculate("max({result}, 10)", ['result' => 7]);
// Result: 10

With Variables

$formula = "{result} * {factor} + 10";
$variables = [
    'result' => 5.2,
    'factor' => 2
];

$result = $calculator->calculate($formula, $variables);
// Result: 20.4

BMI Calculation

$formula = "{weight} / ({height} ** 2)";
$variables = [
    'weight' => 70,   // kg
    'height' => 1.75  // meters
];

$result = $calculator->calculate($formula, $variables);
// Result: 22.86

Gender-Based Calculation

// Apply different multipliers based on gender
$formula = "{result} * (1 + 0.1 * {gender})";
$variables = [
    'result' => 100,
    'gender' => 1  // Female = 1
];

$result = $calculator->calculate($formula, $variables);
// Result: 110

Complex Formula

// Pythagorean theorem
$formula = "(({a} ** 2 + {b} ** 2) ** 0.5)";
$variables = [
    'a' => 3,
    'b' => 4
];

$result = $calculator->calculate($formula, $variables);
// Result: 5

Using calculateFromDefinition

$calcDef = [
    'FormulaCode' => '{result} * {factor} + {gender}',
    'Factor' => 2
];

$testValues = [
    'result' => 10,
    'gender' => 1  // Female
];

$result = $calculator->calculateFromDefinition($calcDef, $testValues);
// Result: 21 (10 * 2 + 1)

Formula Validation

Validate formulas before storing them:

$validation = $calculator->validate("{result} / {factor}");
// Returns: ['valid' => true, 'error' => null]

$validation = $calculator->validate("{result} /");
// Returns: ['valid' => false, 'error' => 'Error message']

Extract Variables

Get a list of variables used in a formula:

$variables = $calculator->extractVariables("{result} * {factor} + {age}");
// Returns: ['result', 'factor', 'age']

Error Handling

The service throws exceptions for invalid formulas or missing variables:

try {
    $result = $calculator->calculate("{result} / 0");
} catch (\Exception $e) {
    // Handle division by zero or other errors
    log_message('error', $e->getMessage());
}

Common errors:

  • Invalid formula syntax: Malformed expressions
  • Missing variable: Variable placeholder not provided in data array
  • Non-numeric value: Variables must be numeric
  • Division by zero: Mathematical error

Best Practices

  1. Always validate formulas before storing in database
  2. Use placeholder syntax {variable_name} for clarity
  3. Handle exceptions in production code
  4. Test edge cases like zero values and boundary conditions
  5. Document formulas with comments in your code

References